wy132
wy132
发布于 2024-07-14 / 2 阅读
0

Java基础 学习笔记

堆,栈,常量池

  1. 栈:存放基本类型的数据和对象的引用(定义名),但对象本身不存放在栈中,而是存放在堆中

  2. 堆:存放用new产生的数据

  3. 静态域:存放在对象中用static定义的静态成员

  4. 常量池:存放常量,如没用new生成的字符串

等号==

==与equals方法(A.equals(B)

equals方法:A,B不是对象类型,则是值判断,如果A,B都是对象,则是对象判断!!!

==:

  1. 如果两边都是数据类型,则根据判断,包括intflaot的判断

  2. 如果两边都是对象类型,则判断是否指向同一对象

  3. 如果一个数据类型,一个对象,则是判断

  4. 如果是对象属性,则是值判断

注释

  1. 单行注释(// 注释文字

  2. 多行注释(/* *注释文字 **/

  3. 文档注释/** **注释文档 \*/JAVA特有(通过Javadoc 生成 API 帮助文档, API 文档相当于产品说明书)

文档注释:主要用来说明类、成员变量和方法的功能

与其他的区别:非java Doc注释给写代码维护代码的人看的

字符串

String支持 ‘+’ 实现字符串拼接(char的‘+’是ac码的相加)

String创建数组定义数组大小会报错

数据类型

布尔型关键字:boolean

java支持数据类型自动转换:

  • 允许把一个数值直接量直接赋给另一种类型的变量

  • String 类型的直接量不能赋给其他类型的变量

  • boolean 类型的直接量只能赋给 boolean 类型的变量

  • StringintInteger.parseInt(str)

  • charstringstr = ch + ""

键盘输入

import java.util.Scanner;           //1.引入输入包
public class mytest {
    public static void main(String[] args) {
        Scanner myScanner = new Scanner(System.in);     //2.创建输入对象
        String name = myScanner.next();                 //3.1对象的next方法获取字符串
        int age = myScanner.nextInt();                  //3.2对象的nextInt方法获取整型数据
    }
}

数组

默认放到

推荐命名方式:

type[] arrayName;    // 数据类型[] 数组名;
//声明数组大小
arrayName = new type[size];    // 数组名 = new 数据类型[数组长度];

在声明数组时不需要规定数组的长度,例如:

int score[10];    // 这是错误的
int[] a = new int[5];   //对
int[] number = new int [5] {1,2,3,4,5};     //错,不能即指定数组的长度,又为每个数组元素分配初始值

支持数组赋值给数组:

int[] a = {1, 2, 3};
System.out.println(a[1]);   //输出2
int[] b = a;    
b[1] = 100;
System.out.println(a[1]);   //输出100
System.out.println(b[1]);   //输出100

注意:数组赋值给数组是以指针形式来赋值

对象

对象分配机制

Cat mycat1 = new Cat(); //创建mycat1指针指向堆的一空间
mycat1.name = "inin";
Cat mycat2 = mycat1;    //mycat2指针指向mycat1的指向,mycat2.name为inin

##重载

定义:同一个类中包含了两个或两个以上方法名相同的方法,但形参列表不同,这种情况被称为方法重载

形参列表不同指个数类型不同或个数类型都相同但顺序不同,返回类型不同不算重载

##方法

可变参数:

public int sum(int... nums) {       //nums相当于未指向的数组指针,接收到数据后创建空间创建数组
    System.out.println(nums.length);    //内置length方法求数组大小
}
​
//与普通参数一起使用
 public int sum(int a, int... nums) {
     //可变参数必须在最后
 }

构造方法(构造器):用来初始化对象实例,一般接着写在类定义的属性后面

​
public static void main(String args[]) {
    Person p1 = new Person("inin", 30);
    System.out.println(p1.name);        //输出inin
    System.out.println(p1.age);         //输出30
}
​
class Person {
    String name;
    int age;
    public Person(String pName, int pAge) { 
        name = pName;
        age = pAge;
    }
}

备注:无返回,不能写void;方法名和类名相同

this关键字:

作用:最大的作用就是让类中一个方法,访问该类里的另一个方法或实例变量

备注:可以访问构造器

this(参数列表);     //只在构造器里面使用,来使用另一个构造器

定义:将处理同一方面的问题的类放在同一个目录下,以便于管理

作用:

  1. 区分相同名称的类。

  2. 能够较好地管理大量的类。

  3. 控制访问范围。

package关键字:声明当前类所在的包,放在类的最上面,只允许只有一个

注意:不同的java文件包名相同表示这些java文件里的类归属于同一个包,

import关键字:导入指定包层次下的某个类全部类

import example.Test;		//导入example包的Test类
import example.*;			//导入example包所有类

常用的包:

  • java.lang.* //基本包,默认引入

  • java.util.* //系统提供的工具包,含Scanner类

  • java.net.* //网络包,网络开发

  • java.awt.* //java的界面开发,GUI

访问修饰符

定义:限定类,方法,属性的访问权

作用:

  • 防止对封装数据的未授权访问(即调用)。

  • 有助于保证数据完整性。

  • 当类的私有实现细节必须改变时,可以限制发生在整个应用程序中的“连锁反应”

的访问控制符:只能是空(friendly默认)或者 public

方法和属性的访问控制符: public(公有)、 private(私有)、protected(保护) 和 friendly(默认、空)

继承

1.子类不可以直接访问父类private属性和方法,要通过父类的公共方法间接访问

2.子类自动调用父类构造器,完成父类的初始化,若父类没有无参构造器,则在子类的构造器中必须用super指定父类的构造器来完成父类的初始化,确保父类被初始化

3.super指定父类构造器,必须写在子类构造器的第一行

4.访问子类属性或方法,先看子类是否有相应的属性和方法,有就调用子类的,没有再向上一级父类访问,即当子类和父类有同名属性和方法时默认先调用子类的

5.子类super则默认调用父类的无参构造,有则不调用,而调用super指定的构造

super

作用:访问父类的属性,方法,构造器

访问父类属性,方法:super.属性名;super.方法名

访问父类构造器:super(参数列表)

//调用的查找顺序
funC();			//查找当前类的的方法,没有则向父类查找,直到object类
this.func();	//同上
super.func();	//直接向父类查找,直到object类

重写

定义:子类中创建了一个与父类中相同名称、相同返回值类型、相同参数列表的方法,但方法体中的代码不同,以实现不同于父类的功能

注意:子类方法返回类型和父类方法的一样,或是父类方法返回的类型的子类,也算重写

class Far {
    public Object func() {
        return	null;
    }
}

class Son extends Far {			//允许
    public string func() {		//Object类是string类的父类
        return null;
    }
}

注意:子类方法不能缩小父类方法的访问权限,若父类方法为public,子类方法不能为private等权限变小的

多态

多态存在的3个条件:继承,重写,父类引用指向子类(Parent p = new Child();

编译类型,运行类型:运行类型必为编译类型的子类

Animal a = new Dog();	//Animal为编译类型, Dog为运行类型
a = new Cat();		//	运行类型可以改变,但编译类型固定不变
a.func();			//先从子类Dog中查找方法,然后再向上查找

方法传参的类的多态:传参是指定类,也可以是指定类的子类

//方法
public void func(Animal animal, Food food) {	//传入的类是Animal,也可以是Animal的子类Cat等
    //输出animal的name,food的name 
}
//调用
a.func(catB, riceC);	//catB为Animal的子类Cat的实例对象,riceC为Food子类rice的实例对象

向上转型:父类引用指向子类:Parent p = new Child();

特点:p具有父类属性和方法,且具有子类的方法,但无子类的属性

注意:可以调用父类成员(遵守访问权限),但不能调用子类的属性,调用方法时会先从子类查找

注意:属性没有重写

instanceOf关键字:判断a对象的运行类型是否为b类型或b类型的子类

System.out.println(a instanceOf b); //是运行类型,不是编译类型

动态绑定机制: 调用对象方法时,对象的所有方法都是从运行类型中开始找,甚至编译类型里的方法里的方法也是从运行类型中找。

多态数组: 符合动态绑定机制,new谁,方法就调用谁

Person persons = new Person[3];	//对象数组
persons[0] = new Person();	
persons[1] = new Student();	//向上转型
persons[2] = new Teacher();	//向上转型
persons[i].say(); 	//调用方法时,遵循动态绑定机制,即先从i类型中的方法找

Object类

equals():比较两个对象的内容是否相等

注意:==运算符是两边是否指向同一实例(是否地址相同),判断new生成的变量时与equals方法有区别

static关键字

static声明的变量或方法叫类变量,类方法,也称静态变量

  • static修饰的成员变量和方法,从属于类

  • 普通变量和方法从属于对象

  • 静态方法不能调用非静态成员,编译会报错,反之可以

  • 静态方法不能用thissuper

特点:

  1. 不依赖对象名访问,直接用 类名.类变量 或 类名.类方法 即可以调用

  2. 被所有对象共享,在内存中只有一个副本,在类初次加载的时候才会初始化

代码块

解析:构造器的补充

语法:

[修饰符] {
    代码
};
  • 修饰符可写可不写,只能写static

  • static的叫静态代码块,没有的叫普通代码块

  • ;可写可不写

  • 先于构造器执行

  • 静态代码块:随则类的加载而执行

  • 普通代码块:创建对象执行,和类加载无关

  • 如果只是调用类的静态成员,普通代码块不被调用

  • 静态代码块与静态成员优先级相同,普通代码块与普通成员优先级相同

  • 父类先于子类执行代码块

  • 构造器最后执行

  • 静态代码块只能调用静态成员,普通代码块任意调用

//子类继承关系时创建实例时调用顺序
1.父类静态代码块,静态成员
2.子类静态代码块,静态成员
3.父类普通代码块,==普通属性==初始化
4.父类的构造方法						//构造器后于普通属性
5.子类普通代码块,==普通属性==初始化
6.子类的构造方法    					//构造器后于普通属性

类什么时候被加载:

  1. new实例

  2. 创建子类实例,父类被加载

  3. 使用类的静态成员


final关键字

相当于const,变量名一般全大写

//修饰类:表示当前类不能被继承
final class A {}

//修饰父类方法:此方法无法被子类覆盖/重写
final void func() {}

//修饰变量:相当于const
final int a = 100;

//修饰数组:表示数组地址不可改变,但值可以改变
final int[] a = {1, 2, 3};

抽象类abstract

关键字:abstract

作用:把子类大致相同的代码抽象出来成抽象类,但可以利用重写的方式让子类实现大致相同中的不同

//修饰类
访问修饰符 abstract 类名 {}
//修饰方法
访问修饰符 abstract 返回类型 方法名(参数) {};  //里面不能写代码
//子类继承了抽象类,必须重写抽象类的所有抽象方法
  • 抽象类不能被实例

  • 抽象类可以没有abstract方法

  • 方法有abstract,类必须声明为abstract

  • abstract只能修饰类和方法,不能修饰属性和其他

  • 抽象类可以有任意成员

  • 抽象方法不能写代码

  • 一个类继承了抽象类,则它必须实现(重写)抽象类的所有抽象方法,除非它也声明为抽象类

  • 抽象方法不能被privatefinalstatic修饰,否则子类无法重写


接口Interface

解析:特殊的抽象类,关键字:Interfacedefaultimplements

interface 接口名 {		//创建接口
    //属性
    //方法:
    //1.抽象方法
    public void func(); //可以省略abstract关键字   相比于抽象类少了中括号
    //2.默认实现方法  可以写代码字体
    default public void func() {	//default关键字
     	代码   
    }
    //3.静态方法
    public static void func() {		
     	代码   
    }
}
//普通类实现接口
class 类名 implements 接口 {	
    //接口成员
    //自己成员
    //重写接口方法
}
//抽象类实现接口
abstract class 类名 implements 接口 {		
    //成员
    //可以不用重写接口方法
}
//普通类实现多个接口
class 类名 implements 接口1, 接口2 {		
    //成员
    //重写接口方法
}
//接口继承多个接口
interface A extends B,C {}
  • 接口不能被实例化

  • 类含有接口的成员

  • 接口方法只能默认被public abstract修饰

  • 普通类实现接口,必须实现接口方法,抽象类则不用

  • 接口中的属性,只能且默认public static final 修饰,如int a = 10(必须初始化)

  • 接口修饰符只能是public和默认

class A extends B implements C {}		//A类继承B且实现C

//举例
interface A {
    int x = 0;		//默认静态
}
class B{
    int x = 1;
}
class C extends B inplements A {
    public void pX() {
        //System.out.println(x);  错误
        System.out.println(A.x + " " + super.x); //正确
    }
    public static void main(String[] args) {
        new C().pX();
    }
}

接口与继承比较:子类继承则自动拥有父类的功能,子类需要扩展则实现接口方式

理解:实现接口 是 对java 单继承机制的一种补充

接口的多态特性:


//接口名Usb,实现接口的类名Phone、Camera
Usb[] usbs = new Usb[2];
usbs[0] = new Phone();		//多态
usbs[1] = new Camera();
for(int i = 0; i < usbs.length; i ++) {
    usbs[i].func();		//动态绑定
    if(usbs[i] instanceof Phone) {		//特有方法调用
        ((Phone)usbs[i].call);
    }
}

类的5大成员:属性,方法,构造器,代码块,内部类

内部类

定义:在类内部的类

局部类

定义在类的局部位置里,如方法内,代码块

局部内部类(有类名)

class Outer {	
    private int n1 = 100;
    private void func1() {}
    public void func2() {		//方法
        final class Inner {		//局部内部类
            public void f2() {
                int n1 = 333;
                System.out.println(n1 + Outer.this.n1);	//访问内部n1和外部n1
                func1();					//访问外部成员
            }
        }
        Inner m = new Inner();		//外部类使用局部类,需创建
        m.f2();		
    }
}
  • 通常定义在方法或代码块中,有类名

  • 可以访问外部任意成员,包括私有

  • 不能添加修饰符,但可以添加final

  • 作用域:在定义的方法或代码块中,外部其他类不能访问

  • 外部类使用局部内部类只能在作用域内使用

  • 局部内部类与外部类成员重名时,调用遵循就近原则,可用外部名.this.成员名形式调用外部成员

匿名内部类(无类名)

实现接口继承类 ,简单创建一个仅用一次的类

class Outer {
    //匿名类实现接口:
    IA tiger = new IA() {	//内部会自动创建一个匿名类实现IA接口然后返回实例给tiger,然后匿名类被销毁
        @Override
        public void cry() {		//该重写的还是要重写
            //
        }
    };	//有分号
    tiger.cry();	
    
    new IA(){
        @Override
        public void cry() {		//该重写的还是要重写
            //
        }
    }.cry;		//也可以直接这样调用cry
    
    //匿名类继承类
    Father father = new Father("jack") {	//并传参
        
    };
    
}

class Father {
    
}

interface IA {
    public void fun();
}
/*
class B implements A{} 	//以往需要写个类实现接口,然后再创建实例
						//而匿名内部类在内部创建类并返回实例然后销毁,简化开发
*/

注意:tiger的编译类型为IA,运行类型为已销毁的匿名类

  • 可以访问外部任意成员,包括私有

  • 作用域:在定义的方法或代码块中,外部其他类不能访问

  • 外部类使用局部内部类只能在作用域内使用

  • 局部内部类与外部类成员重名时,调用遵循就近原则,可用外部名.this.成员名形式调用外部成员

匿名内部类实践:

public class A {
    public static void main(String[] args) {
        f1(new IL() {	//返回一个实现接口IL的类的实例
            @Override
            public void show() {
                //
            }
        });
    }
    public static void f1(IL il) {		//形参是接口IL,但传进去的是实现接口的匿名内部类
        il.show();
    }
}
interface IL {
    public void show();
}
/*
传统方法
定义一个class p实现IL,再f1(new p)
*/

成员内部类

定义在外部类的除方法代码块的地方

class A {
    class B {} 		//成员内部类
}
  • 可以访问外部任意成员

  • 可以添加任意修饰符

  • 作用域在外部类

  • 外部类访问成员内部类的成员:先创建内部类再调用,私有成员也可调用

  • 成员内部类与外部类成员重名时,调用遵循就近原则,可用外部名.this.成员名形式调用外部成员

外部其他类可以访问成员内部类:

public class A {
    public static void main(String[] args) {
 		A a = new A();		//先创建A类
        B b = A.new B();	//再创建B类
    }
}

class A {
    class B{}
}

静态内部类

定义在外部类的除方法代码块的地方,有static修饰

  • 可以访问外部所有静态成员,包括私有的,不能访问非静态成员

  • 可以添加任意修饰符


枚举类

enum Color 		//自动继承Enum类
{ 
    RED, GREEN, BLUE; 
} 
//使用
...
Color c1 = Color.RED;
Soprint(c1);	//输出RED
...

枚举本质:定义类,并在内部创建静态final类

class Color
{
     public static final Color RED = new Color();
     public static final Color BLUE = new Color();
     public static final Color GREEN = new Color();
}

for和switch中使用:

for (Color myVar : Color.values()) {
    System.out.println(myVar);
}


Color myVar = Color.BLUE;
switch(myVar) {
   case RED:
     System.out.println("红色");

枚举类内置方法:

  1. values(): 返回枚举类中所有的值。

  2. ordinal():方法可以找到每个枚举常量的序号,就像数组索引一样。

  3. valueOf():方法返回指定字符串值的枚举常量。

自定义类的枚举

步骤:

  1. 将构造器私有,防止直接new

  2. 去掉setXxxx方法,防止属性被修改

  3. 在类的内部直接创建静态固定对象

  4. 可以添加final

class Season {
    private String name;
    public static final Season SPPING = new Season("春天");		//全大写
    public static final Season WINTER = new Season("冬天");
    public static final Season AUTUMM = new Season("秋天");
    public static final Season SUMMER = new Season("夏天");
    private Season(String name) {} //构造器
}

//方法2
//public static final Season SUMMER = new Season("夏天");用SPPING("春天");代替
enum Season {
    SPPING("春天");
    WINTER("冬天"), AUTUMM("秋天"), SUMMER("夏天");
    private String name;
    private Season(String name) {} //构造器
}
//1.使用的是enum不是class
//2.如果有多个,可以用,间隔
//3.要将SPPING("春天")写在最前面
  • 可以提供get方法,但不要提供set方法


注解

Override注解:写在方法前,表示重写

Deprecated注解:用于表示某个类,方法已过时。可以修饰方法,类,字段,包,参数等

SuppressWarning注解:用于抑制运行时的警告信息


异常处理

作用:当出现程序错误时执行另一段代码,而不是直接程序崩溃运行不了,可以让程序具有更好的容错性,是程序更加健壮

异常类型:主要是编译类型、运行类型

常用异常分类图:

  • Throwable类:是所有异常和错误的超类

  • Error:定义了在通常环境下不希望被程序捕获的异常。一般指的是 JVM 错误

    • StackOverflowError :栈溢出

    • OutOfMemoryError :内存耗尽

  • Exception:类用于用户程序可能出现的异常情况,它也是用来创建自定义异常类型类

    • RuntimeException:运行时异常

      • NullPointerException:访问了一个NULL对象成员,空指针异常

      • ArithmeticException:算术错误异常,如0作除数

      • ArrayIndexOutOfBoundsException:数组索引越界

      • ClassCastException:类型转换异常

      • NumberFormatException:数字转化格式异常,比如字符串到 float 型数字的转换无效

    • FileNotFoundException :未找到文件

    • ClassNotFoundException:没有找到类

try-catch(finally可不写)

try {
    //代码1
    //代码2
} catch(异常类型 e) {
    //代码3
} finally {			//不管是否异常,都执行代码4
    //代码4
}

//try后接多个catch
try {    
} catch(异常类型1 e) {    
} catch(异常类型2 e) {    
}

//没catch
try {
} finally {		//若出现异常则throw给上级处理
}
  • 如果代码1发生异常,后面的代码2不会执行,跳到catch

  • catch必须指明异常类型

  • try后面可接多个catch

  • 可以只有try+finallycatch,相当于throws

  • catchreturnfinally也有return,最终返回的是finally

throws与throw

throws

public void func() throws 异常类型 {} 	//主要用在方法中,异常类型可以是异常的父类
public void func() throws 异常类型1,异常类型2 {} 	//可以多个类型
  • 对于运行类型异常,默认throws方法处理

  • 子类重写父类方法时,其抛出类型必须与父类方法的一致,或者是父类方法的异常类型的子类

  • 方法1调用方法2时,若编译类型异常,方法1也必须throws异常类型,运行类型则不用

throw:

throw new 异常类型("xxxx"); //向上级,或try抛出异常

throws与throw的区别:

throw与try同时使用

try {
    throw new 异常类型1("xxxx");
} catch (异常类型1 e) {
    
} catch (异常类型2 e) {	//try里面还可能出现其他类型
    
}

自定义异常

if(xxx) {		//如果满足xxx条件就抛出异常
    throw new 异常类型1("xxxx");
}

八大包装类

包装类与基本数据类的转换:

int m = 100;
Integer im = m;	//自动装箱,即int类的值直接赋给Integer
int n = im;		//自动拆箱,相反
//例题
Object obj = true?new Integer(1) : new Double(2);
System.out.println(obj);	//输出1.0,三元运算符是整体,Double会提高精度

包装类型与String类的转换:

//包装类->String
Integer i = 10;
String s1 = i.toString();		//方法1
String s2 = String.valueOf(i);	//方法2
String s3 = i + "";				//方法3
//String->包装类
Integer j = new Integer(s1);	//方法1
Integer k = Integer.valueOf(s2);//方法2

Integer等包装类有大量内置方法


String类

##String

创建:

String s1 = "inin";					//方法1
String s2 = new String("inin");		//方法2,与方法1有不同
//s1[0]错误,需用方法获取
  • 方法1是直接在常量池中操作数据;而方法2是在堆中创建空间操作常量池的数据

  • 注意: == 是判断两边是否都在堆同一地址或都在常量池同一地址

  • String c = "in" + "in" 常量相加,看的是

  • String c = a + b 变量相加,是在中,则是在堆中创建空间指向ab中的数据,类似于方法2

  • 对象中的String是属于在中,如person.name 比较的是堆,不是常量池

常用方法:

StringBuffer

与String的区别:

String创建后一旦修改值,则在常量池中新开辟地址存放新的值,然后重新指向新的值,本质是更改地址;StringBuffer值存放在中,不用更改地址

StringBuffer s1 = new StringBuffer();		//默认长度为16
StringBuffer s2 = new StringBuffer(100);	//指定长度
StringBuffer s3 = new StringBuffer("inin");	//长度为 输入的字符串 + 16		

String 与StringBuffer转换:

//String 转 StringBuffer
String str = "inin";
StringBuffer nstr = new StringBuffer(str);	//str本身没影响
//StringBuffer 转 String
StringBuffer s = new StringBuffer("inin");
String b1 = s.toString();	//方法1
String b2 = new String(s);	//方法2

StringBuilder

特点:类似StringBuffer,非线程安全,比StringBuffer快,但建议用在字符串缓冲区被单个线程使用的时候

比较

Arrays类

内含许多数组操作的方法,可直接通过Arrays.xxx(xxx)方法调用

常用方法:

  1. int binarySearch(type[] a, type key):在已升序排序好的数组a中查询key,返回其位序,若空则返回负数

  2. type[] copyOf(type[] original, int length):复制数组

  3. boolean equals(type[] a, type[] a2):两数组是否相等

  4. void fill(type[] a, type val):val赋值给a所有元素

  5. void sort(type[] a):升序排序,底层原理:双轴快排

  6. String toString(type[] a):将数组转换为字符串,通常用于输出

其他类

System类、BigInteger类:处理较大的整数、BigDecimal:处理精度较高的小数

集合

Vector

解析:动态数组

与ArrayList区别:

  1. Vector 使用了 Synchronized 来实现线程同步,是线程安全的,而 ArrayList 是非线程安全的

  2. ArrayList 在性能方面要优于 Vector,因为没有Synchronized加锁

  3. ArrayList扩容增加50%,Vector默认100%(可自定义扩多少倍)

  4. Vector类的所有方法都是同步的,可以由两个线程安全地访问一个Vector对象

ArrayList

解析:没有固定大小的数组,查询效率高,增删效率低

import java.util.ArrayList; // 引入 ArrayList 类
ArrayList<E> objectName =new ArrayList<>();  // 初始化,E是数据类型

注意:E只能为引用类型,即八大包装类

常用方法:

objectName.add(X);			//添加x到数组中	
objectName.get(1);			//取值objectName[1]
objectName.set(1, X);		//插入X到objectName[1]
objectName.remove(3);		//删除objectName[3]
objectName.size();			//数组长度
Collections.sort(objectName);//排序

LinkedList

底层实现:链表,增删效率高,查询效率低

import java.util.LinkedList; // 引入 LinkedList 类
LinkedList<E> list = new LinkedList<E>();   // 普通创建方法
LinkedList<E> list = new LinkedList(Collection<? extends E> c); // 使用集合创建链表	

特有方法:

TreeSet

底层实现:红黑树

特点:有序,不可重复

HashSet

底层实现: HashMap

特点:无序,不可重复,不是线程安全

Hashtable

HashMap

TreeMap

底层实现:红黑树,二叉树

特点:有序,键不可重复,值可以重复

泛型

常用于接口或类

class Person<E> {			//泛型类一般用于类中的属性类型不确定的情况下
    E data;
    public Person(E data) {
        this.data = data;
    }
}
  • T、E只能是引用类型(包装类),

  • 泛型指定具体类型后,可传入该类型的子类类型,例:

    Person<Father> a = new Person<Father>(new Son())

  • 简写:Person<Father> a = new Person<>()

自定义泛型类:

class Person<T, R...> {	//可以有多个泛型
    T sum;
}
//创建实例
Person<T, R..> tom = new Person<>(T);   
  • 使用泛型数组,不能初始化,因为不知道其数据类型,不知道开辟多大空间

  • 静态方法不能使用类的泛型

  • 创建实例时,自动转换

  • 创建对象时指定泛型的类型,没有指定则默认为Object

自定义泛型方法:

public<A> void func(A a) {	//接在在修饰符后面
    
}
//与方法使用泛型区别
public E func(E e) {}

//传参时,自动转换成包装类
func(100);	//转换成Integer

泛型继承和通配符:

  • 泛型不具备继承性:Person<Object> person = new Person<String>(); 错误

  • <?>:表示可以传入任意泛型

  • <? extends A>:支持A类以及A类的子类,规定了泛型的上限

  • <? super A>:支持A类以及A类的父类,规定了泛型的下限


反射

java程序运行总共有三个阶段:

  1. 代码阶段/编译阶段:javac将代码编译成字节码文件

  2. Class类加载阶段:类加载器将字节码文件的类加载到堆中的Class类中

  3. 运行阶段:创建对象实例时,该对象知道他是属于哪个Class对象

反射:

根据字符串名获得其对应的类对象实例

Class Dog {  }
Class c1 = Class.forName("Dog");
//方法2:对象.getClass()
//方法3:任何类型.class

根据字符串名获得对应的方法对象实例

Class Dog { func() }
Class c1 = Class.forName("Dog");
Method method1 = c1.getMethod("func");

通过方法对象调用方法

method1.invoke(oname);
//方法.invoke(对象)
//表示调用oname对象里的method1方法
//与传统的对象名.方法名不同
Dog dog1 = new Dog("jj", 12);
Dog dog2 = new Dog("kk", 24);
Class d1 = Class.forName("Dog");		//获取类实例d1,包含Dog类的属性和方法
Method m1 = d1.getMethod("getName");	//获取Dog类中名为getName的方法实例m1
System.out.println("===" + m1.invoke(dog1) + "=====");	//调用方法实例m1,用于dog1实例
System.out.println("===" + m1.invoke(dog2) + "=====");	//调用方法实例m1,用于dog2实例

输出:

image-20240419202458226

多线程

image-20240412211724493

  • 运行java程序,会创建一个进程

  • 进程会创建一个main线程,运行main主方法

  • 如果有创建线程,则main会创建线程T0

  • 进程什么时候结束:所有线程都运行完

    • 若main主线程运行完,T0线程还运行,则进程不结束

    • 若T0主线程运行完,main线程还运行,则进程不结束

  • 主线程可创建多个子线程

  • 子线程也可创建多个线程

实现Runnable接口来创建线程:

image-20240412215455637

  1. 创建类T3继承Runnable接口,并重写run方法

  2. main中创建T3实例t3

  3. 再创建线程Thread类的实例thread1和2,调用Thread类的start方法创建线程

线程常用方法:

image-20240412231334576

image-20240413113810317

  • join 方法:主线程使用join方法表示:主线程进入阻塞状态,直到子线程运行结束后,主线程继续运行

image-20240413115417295

  • 将子线程设置为守护线程:thread1.setDaemon(true)

  • 特点:主线程结束后,无论子线程(守护线程)是否永久执行,都会跟着结束

线程状态:

线程状态图

  1. 初始(NEW):新创建了一个线程对象,但还没有调用start()方法。

  2. 运行(RUNNABLE):Java线程中将就绪(ready)和运行中(running)两种状态笼统的称为“运行”。 线程对象创建后,其他线程(比如main线程)调用了该对象的start()方法。该状态的线程位于可运行线程池中,等待被线程调度选中,获取CPU的使用权,此时处于就绪状态(ready)。就绪状态的线程在获得CPU时间片后变为运行中状态(running)。

  3. 阻塞(BLOCKED):表示线程阻塞于锁

  4. 等待(WAITING):进入该状态的线程需要等待其他线程做出一些特定动作(通知或中断)。

  5. 超时等待(TIMED_WAITING):该状态不同于WAITING,它可以在指定的时间后自行返回。

  6. 终止(TERMINATED):表示该线程已经执行完毕。

多线程访问(运行)同一对象:

Dog dog = new Dog();
Thread thread1 = new Thread(dog);
Thread thread2 = new Thread(dog);
Thread thread3 = new Thread(dog);
  • 表示创建三个线程,其都运行dog对象,从而引出同步互斥概念

  • 类似线程都指向在内存中的对象,以指针指向的方式,非各复制一个对象

java8新特性

Lambda与方法引用与函数式接口为同一类知识

Lambda与方法引用多用于函数式接口

  • Lambda 表达式 − Lambda 允许把函数作为一个方法的参数(函数作为参数传递到方法中)。

简化编写代码

语法

(参数) ->(函数或语句)
// 1. 不需要参数,返回值为 5  
() -> 5  
  
// 2. 接收一个参数(数字类型),返回其2倍的值  
x -> 2 * x  
  
// 3. 接受2个参数(数字),并返回他们的差值  
(x, y) -> x – y  
  
// 4. 接收2个int型整数,返回他们的和  
(int x, int y) -> x + y  
  
// 5. 接受一个 string 对象,并在控制台打印,不返回任何值(看起来像是返回void)  
(String s) -> System.out.print(s)

  • 方法引用 − 方法引用提供了非常有用的语法,可以直接引用已有Java类或对象(实例)的方法或构造器。与lambda联合使用,方法引用可以使语言的构造更紧凑简洁,减少冗余代码。指向一个方法

//例子
Comparator<String> com1 = new Comparator<String>() {
    @Override
    public int compare(String o1, String o2) {
        return o1.compareTo(o2);
    }
};

Comparator<String> com1 = String :: compareTo;
//表示,创建一个Comparator函数式接口的对象com1,用String类里的compareTo方法来重写
System.out.println(com1.compare("abc", "abb"));

//例子
//计算器函数式接口
@FunctionalInterface
public interface Calculator {
	//抽象方法作用:求绝对值
	int getAbs(int num);
}
public class Abs {
	public static void main(String[] args) {
//		使用Lambda表达式
		method((int num) -> {
			int result;
			if (num>=0) {
				result = num;
			}else {
				result = -num;
			}
			return result;
		});
		
//		方法引用的意义:Math类当中有一个abs静态方法,已经有了现成的功能,直接拿过来用
		method(Math::abs);//将Math里的abs方法传入method方法
	}
	
	public static void method(Calculator cal) {//函数作参数
		int result = cal.getAbs(-25);
		System.out.println("结果是"+result);
	}
}
  • 函数式接口:指接口只声明一个抽象方法

@FunctionalInterface
interface GreetingService 
{
    void sayMessage(String message);
}
  • 默认方法 − 默认方法就是一个在接口里面有了一个实现的方法。

  • 新工具 − 新的编译工具,如:Nashorn引擎 jjs、 类依赖分析器jdeps。

  • Stream API −新添加的Stream API(java.util.stream) 把真正的函数式编程风格引入到Java中。

应用场景:java层面处理redis等NoSQL型数据库

让你以一种声明的方式处理数据

是一个类

终端操作(例如 Stream.forEachIntStream.sum )可能会遍历流以产生结果或副作用。执行终端操作后,stream pipeline被认为消耗掉了,不能再使用;如果需要再次遍历同一个数据源,则必须返回数据源获取新的流

  • Date Time API − 加强对日期与时间的处理。

  • Optional 类 − Optional 类已经成为 Java 8 类库的一部分,用来解决空指针异常。

  • Nashorn, JavaScript 引擎 − Java 8提供了一个新的Nashorn javascript引擎,它允许我们在JVM上运行特定的javascript应用。