JavaSE基础入门

JavaSE

1、Java的基本介绍

  • JDK:java开发工具包
  • JRE:java运行时环境
  • JVM:java虚拟机

1、Java的特点

  • 跨平台:一次编译,到处运行
  • 安全性
  • 健壮性

2、Java的体系平台

  • JavsSE:基础版
  • JavaEE:企业版
  • JavaME:微型版

3、环境搭建

  • 3.1、安装jdk
  • 3.2、配置环境变量
    • JAVA_HOME:jdk安装的bin目录的上一级

4、第一个Java程序

public class HelloWorld{
    public static void main(String[] args){
        System.out.println("helloworld");
    }
}

注意:

1.java所有的标点符号必须是英文半角
2.java严格区分大小写
3.编码不一致会中文乱码
4.所有的语句以分号结束
5.括号和引号是成对出现的
6.一个java文件内可以存在多个类 编译后有几个类就会生成几个.class文件
7.被public 修饰的类 类名必须与源文件的名字保持一致

5、注释

//单行注释
/*
多行注释
*/

/**
*文档注释
*/

6、标识符

6.1、命名规范:

1.字母数字 下划线_ 美元符$组成
2.数字不能开头
3.不能是java中的关键字和保留字
关键字: 在java中赋予了特殊含义的字符串(单词) class void static
保留字: 当前版本无意义以后可能使用 goto
4.严格区分大小写 String string
5.标点符号必须是英文半角

6.2、命令规范,约定成俗

1.见名之意
2.对类进行命名时要求每一个单词的首字母都大写
HelloWorld PrintTest IdentifierTest
3.对变量,方法命名时 第一个单词首字母小写从第二个单词开始首字母大小
userName
passWord
getSumResult
4.对包进行命名时 每一个单词所有的字母都小写
com.wu.oop
com.wu.thread
com.wu.io
5.对常量命名时 每一个单词所有的字母都大写 如果是多个单词组成 单词与单词之间使用下划线连接

7、基本数据类型

7.1、基本数据类型:四形八种
整形 默认值 字节(1字节8位) 取值范围
byte 0 1 -128~128
short 0 2 -32768 ~ 32767
int 0 4 -2147483648~2147483647
long 0 8
字符形
char \u0000 1
浮点型 存储的数据最多
float(单精度) 0.0 4
double(双精度) 0.0 8
布尔型
boolean false 1
7.2、引用数据类型
  • 只要不是基本数据类型就是引用数据类型
  • 引用数据类型默认值为null

8、常量和变量

8.1、常量:即在程序运行过程中,值不会发生改变的量

​ 8.1.1、自定义常量:用 final 修饰哦

​ 8.1.2、字面量:即编程语言中表示固定值或数据的直接文本形式(eg:10)

8.2、变量:在程序运行过程中,值会发生改变的量

​ 8.2.1、变量的声明和赋值

​ 方式一:先声明再赋值 ------> int i; i = 10;

​ 方式二:声明时赋值 ---------> int i = 10;

注意:

1、局部变量(局部变量:方法内的变量)使用前必须进行初始化(赋值就是初始化)
2、同一个方法内不能存在同名的变量
3、声明变量的本质:申请内存

2、基本语法

1、基本数据类型转换

  • 自动类型提升
    • 小时数据类型的值,赋值给大的数据类型
    • 多个基本数据类型做运算时,结果为运算中的最高类型
  • 强制类型转换:
    • 将大的数据类型的值,赋值给小的数据类型
    • 小的数据类型 变量名 = (小的数据类型)大的数据类型的值 (eg: int i = (int) 10.21; )

2、运算符

2.1、逻辑运算符

& : 与

| : 或

^ : 异或

! : 取反

&& : 短路与

|| : 短路或

2.2、条件运算符:?
  • 10>21?‘假’:‘真’ 三目运算
2.3、位运算符

& : 雨

| : 或

^ : 异或
: 非

<< : 左移

>> : 右移

>>> : 无符号右移

3、键盘输入

//创建键盘输入对象
Scanner input = new Scanner(System.in);
//给出提示语句
System.out.println("请输入");
//获取一个整形的数据,并赋值给 num
int num = intput.nextInt();
/*
获取其他类型的基本数据
nextByte()
nextShort()
nextInt()
nextFloat()
nextDouble()
nextBoolean()

获取字符串数据
next() ---> 遇到空格就结束输入
nextLine()

获取字符类型的数据
使用next()或者nextLine()获取数据后使用charAt(0)方法,来获取输入数据的第一个字符
*/

4、选择语句

4.1、if
//单分支
if(布尔表达式){
    
}
//双分支
if(布尔表达式){
    
} else {
    
}
//多分支
if(布尔表达式){
    
} else if (布尔表达式) {
    
} ......{
    
} else {
    
}
4.2、switch
switch(表达式){
	case 常量值:
        break; //结束循环
    case 常量值:
    	break; //结束当前循环,进行下一次循环
    case 常量值:
    	break;
    [default]: //可有可无,如果条件都不满足的话,默认执行default中的语句
        break;
}

5、循环语句

5.1、for
for(初始变量;循环条件;迭代条件){
	循环体
}
for(int i = 0;i < 5;i++){
    System.out.println(i);
}

5.1.1:嵌套循环:将 一个循环作为另一个循环的循环体

​ 外层循环控制行数

​ 内层循环控制列数

​ 外层他循环执行一次,内层循环需要执行一遍

//冒泡排序及优化
//arr.length-1,一个长度为arr.length的数组只需要执行 arr.length-1次即可有序
for(int i = 0; i < arr.length-1;i++){ 
    boolean flag = true;
    for(int j = 0;j < arr.length-i-1;j++){ //arr.length-i-1-->让排序只注重于无序部分
        if(arr[j] > arr[j+1]){
            int temp = arr[j];
            arr[j] = arr[j+1];
            arr[j+1] = temp;
            flag = false;
        }
    }
    if(flag){
        berak;//如果提前排序完,就提前退出
    }
}

Arrays.toString(arr);
5.2、while
while(循环条件(布尔表达式)){
    
}
5.3、do while
do{
    循环体  //无论条件是否满足,都会执行一次do方法体中的内容
}while(循环条件);
5.4、break,continue,return
  • break:结束循环
  • continue:结束当前循环,进行下一循环
  • return:结束当前方法

6、数组

  • 数据即为存储相同类型数据的有序集合
6.1、数组的声明

​ 数据类型[] 数组名; eg: int[] arr;

​ 数据类型 数组名[] ; eg: int arr[];

6.2、数组的初始化

​ 6.2.1、静态初始化:完成初始化后知道数组的元素内容

​ 形式一:数据类型 数据名[] = {值1,值2};

​ eg:int arr[] = {1,2,3,4,5};

​ 形式二:数据类型 数据名[] = new 数据类型[]{值1,值2};

​ eg; int arr[] = new int[]{1,2,3,4,5};

​ 6.2.2、动态初始化:完成初始化后知道数组的长度和默认值,但不知道具体的值

​ 数据类型 数据名[] = new 数据类型[数组长度];

​ eg:int arr[] = new int[10];//创建一个长度为10的一维数组

6.3、数组的遍历
//普通for循环:需要使用到数组下标
for(int i = 0;i < arr.length;i++){
    System.out.println(arr[i]);
}
//foreach循环(增强for循环,本质也是普通for循环):不需要数组的下标
for(数组元素类型 变量名:数组名){
    System.out.println(变量名)
}
//eg:
for(int i :arr){
    System.out.println(i);
}

注意

1.、获取指定下标的元素 数组名[下标]
2、修改指定下标的值 数组名[下标]=值;
3、获取数组的长度 数组名.length,数组的长度一旦完成初始化 则不可改变
4、下标的范围[0,length-1]
5、一旦下标超过范围 报 java.lang.ArrayIndexOutOfBoundsException 数组下标越界异常

6、数组也可以存引用数据类型

//数组的反转
int arr[] ={1,3,10,21,12,26,15};
for (int i=0,j=arr.length-1;i<j;i++,j--){
    int temp = arr[i];
    arr[i] = arr[j];
    arr[j] = temp;
}
System.out.println("Arrays.toString(arr) = " + Arrays.toString(arr));
6.4、二维数组
  • 数组元素是一维数组数组

6.4.1、二维数组的声明

​ 数据类型[][] [] [] 数据名

​ 数据类型[][] [] 数据名[]

​ 数据类型[][] 数据名[][] [] []

6.4.2、二维数组的初始化

​ 静态初始化

​ 形式一:数据类型 数据名[] [] ={{1,2},{2},{12,3,3}}; 声明和赋值必须一起

​ 形式二:数据类型 数据名[] [] = new 数据类型[] [] {{1,2,3},{1,2},{2,3}}; 声明和赋值必须分开

​ 动态初始化

​ 形式一:数据类型 数据名[] [] = new 数据类型[值1] [值2];

​ 值1:二维数组中一维数组的个数

​ 值2:二维数组中,一维数组元素的个数

​ 形式二:数据类型 数据名[] [] = new 数据类型[值1] [];

​ 值1:二维数组中一维数组的个数

​ 声明了一维数组的数量,具体的一维数组数据需要进行单独赋值来完成初始化

注意

1、二维数组的长度 数组内一维数组的个数
2、获取二维数组元素 数组名[一维数组的下标] [一维数组内元素的下标]
3、二维数组的元素是一维数组 一维数组不是基本数据类型所以 默认值为nul

6.4.3、二维数组的遍历

//普通for
for(int i = 0;i < 二维数据的长度;i++){
    for(int j = 0;j < 二维数组中一维数组的长度;j++){
        System.out.println(arr[i][j]);
    }
}
//增强for
for(二维数组元素类型 变量名1:数组名){
    for(一维数组的元素类型 变量名2:变量名1){
        System.out.println(变量名2);
    }
}

3、面向对象

1、方法

  • 对功能的封装来实现对代码的复用
1.1、静态方法的声明和调用
//声明
[权限修饰符] static 返回值类型 方法名([形参列表]){
    方法体
}
//调用
方法名();

注意

1.方法调用才会执行
2.方法与方法时兄弟姐妹关系 地位相同 在类中声明
3.方法执行完毕回到方法调用处 (如果方法没有返回值 则回到调用方法的下一行)

1.2、方法的重载
  • 在同一个类中,且方法名相同
  • 形参列表不同(形参的数量,类型,顺序不同)
  • 返回值类型不同不构成重载
1.3、可变参数
  • 当实参的数量不确定,且类型都相同时
public void test(String...args){
}

注意:

1.数据类型…形参名 声明可变参数
2.可变参数底层使用数组进行数据存储
3.可变参数接收的实参数量[0,n]
4.可变参数必须位于参 数列表的最后 因此一个方法只能有一个可变参数

1.4、实例方法
  • 没有static修饰的方法
  • 实例方法是属于对象的,必须通过对象名.方法名()来调用
  • 实例方法内含有隐藏变量 this ,this代指吊桶方法的对象

1.5、成员变量(属性/实例变量) 和 局部变量的区别

  • 代码位置不同:成员变量在类中方法外,局部变量在方法类
  • 内存位置不同:成员变量在堆中,局部变量在栈中
  • 默认值问题:局部变量必须初始化后才能使用
  • 作用域不用:成员变量作用在类内,局部变量作用在方法类
  • 修饰符不同:成员变量可以用修饰符修饰,局部变量不行
  • 生命周期不同:成员变量随着类的销毁而销毁,局部变量随着方法的执行完毕而销毁

2、封装

  • 把客观的事物封装成类,即类的私有化,只向外界提供一些简单地接口来对类进行操作
public class Student {
    private String name;
    private int age;
	//get set方法
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
    //无参构造
    public Student() {
    }
    //有参构造
    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }
}
2.1、构造器
  • 快速给成员变量赋值
  • 一个类中如果没有构造器则编译器自动生成一个隐藏的无参的构造器 但是一旦显示声明一个构造器后隐藏的无参的构造器自动消失
  • 构造器可以重载
  • 可以使用this()调用本类中的构造器 且必须位于构造器体的第一行
  • 通过new关键字调用构造器

3、继承

  • 实现资源的复用(描述的是is a的关系)
class 子类 extends 父类{
    
}

1、实现继承后可以复用父类的资源(属性和方法)

2、java是单继承的,一个类只能继承另一个类,但是一个类可以有多个子类(但是可以多实现,或者使用内部类来实现需要多继承的资源)

3.1、重写
  • 子类因为父类的方法无法满足需求时,重写父类的方法

重写注意点

1、权限修饰符 必须 大于等于 父类的权限修饰符

2、void和基本数据类型必须和父类保持一致,引用数据类型可以和父类保持一致,也可以是父类的子类

3、方法名和形参列表必须和父类保持一致

4、编译时异常,子类不能抛出别父类更大的异常

3.1.1、重写toString

  • 可以快速展示属性值
@Override
public String toString() {
    return "Student{" +
            "name='" + name + '\'' +
            ", age=" + age +
            '}';
}
3.2、继承状态下,构造器的执行
  • 每个构造器的首行 都有 super() 调用父类的无参构造器
  • 使用super(参数) 来调用分类的有参构造时,必须方法方法的首行
3.3、this和super关键字

this:代指当前对象

​ 调用属性

​ 调用方法

​ 调用构造器

super:代指当前对象从父类继承的资源

​ 调用父类的属性

​ 调用父类的方法

​ 调用父类的构造器

变量:就近一致

方法:追根溯源

3.4、对象数组
  • 用来存储对象的数组
Student stu1 = new Student();
Student stu2 = new Student();
Student stu3 = new Student();
Student stu[] = {stu1,stu2,stu3};

4、多态

  • 指同一个行为或方法,在不同的对象具有多个不同的表现形式或形态的能力
4.1、多态的形式

4.1.1、向上转型

  • 父类类型 指向子类的对象

1、有继承关系

2、有方法重写

//父类类型 对象名 = new 子类类型();
Person p = new Student();

完成向上转型后 对象 调用方法的判断

编译看父亲,运行看孩子

1、如果子类重写了父类的方法,则执行子类重写的方法

2、如果没有重写,则执行从父类继承的方法

应用:

1、数组,父类类型做数组类型,可以接收任意子类的对象

2、形参,父类类型做形参,可接收任意子类对象

3、返回值,父类类型做返回值,可以返回任意子类对象

缺点:完成向上转型后,无法使用子类独有的方法

4.1.2、向下转型

  • 场景:在完成向上转型后,还想使用子类独有的方法
  • 前提:完成了向上转型
  • 语法:子类类型 变量名 = (子类类型) 父类类型的值;
  • 可能的遇见问题:ClassCastException(类型转换异常)
    • 解决方法:使用instanceof来进行判断
    • 语法: 对象名 instanceof 类型名 —> 判断左边的对象是否属于右边的类型
    • 只要类型在对象的继承的关系上,就会返回true
4.2、多态和属性
  • 多态只和方法有关,可以将子类的属性看做是子类独有的资源,想要使用独有的资源必须进行向下转型

多态方法的调用问题:

虚方法:可以重写的方法

​ 静态分配:编译时 形参和实参最匹配

​ 动态绑定:运行时是否有重写

非虚方法:不可以重写的方法

5、静态

5.1、静态属性和非静态的区别
内存位置 修饰符 调用方法 内存空间 生命周期
静态 方法区 有static修饰 类名.属性名 所有静态共有一份空间 类加载是创建,类销毁时销毁
非静态 无static修饰 对象名.属性名 每创建一个对象,开辟一个空间 对象创建时创建,当没有对象指向堆中的内存时,由GC回收
  • 相同点
    • 代码位置相同:都是类中方法外
    • 默认值:都有默认值
5.2、静态方法和实例方法的区别
调用方式 调用资源 this和super 方法重写 绑定
静态方法 类名.方法名 不能直接使用非静态资源,可以在静态方法内创建对象来调用 不可以使用 没有重写 编译时绑定
实例方法 对象名.方法名 可以使用所以资源 可以使用 可以重写 运行时绑定
  • 调用方式:在同一类中 实例方法可以省略对象名 静态方法可以省略类名
5.3、静态导入
  • 导入类中所有的静态资源
    • import static 包名.类名.*;
    • 作用:使用时不需要使用类名,直接写资源名
5.4、final和native关键字
  • final
    • 修饰类 :不能有子类
    • 修饰方法:方法不能重写
    • 修饰变量:变为常量
      • 基本数据类型:数值不能修改
      • 引用数据类型:地址不能改变
    • 修饰成员变量时,必须要赋值
  • native
    • 本地方法,没有方法体,主要时用来调用其他语言编写的用来运行电脑程序的代码
    • 存在本地方法栈内

6、抽象

6.1、抽象类
  • 被abstract修饰的类
    • 不能创建对象
    • 抽象类中可以存在普通方法,属性以及构造器
6.2、抽象方法
  • 被abstract修饰的方法 — > abstract 返回值 方法名();

  • 如果一个类继承了抽象类

    • 要么实现抽象类中的所有方法
    • 要么此类也变为抽象类

    抽象方法必须在抽象类中

7、接口

  • 是一种规范,表达的是 has a或者like a的关系
7.1、基本使用
//接口中的方法全部为抽象方法
public interface Person{
    //抽象方法
    void eat();
    //接口中可以用默认方法
    default void sleep(){
        System.out.println("睡觉");
    }
}

//实现接口,接口可以多实现
public class Student implements Person{
    //实现接口后必须重写接口中的抽象方法
    @Override
    void eat(){
        
    }
}

7.2、接口中的成员
  • 抽象方法:默认被public abstract 修饰
  • 全局静态常量:默认被public static final修饰
  • 静态方法:默认被public修饰
  • 默认方法:默认被public修饰

注意:

1、如果一个类实现了接口,可以看过这个接口的孩子

2、一个类可以实现多个接口

3、如果实现了多个接口,这些接口中含有相同方法名默认方法,实现类必须重写该默认方法

​ 调用接口内同名的默认方法,接口名.super.默认方法名(); eg: Person.super.sleep();

4、如果既要实现接口,也要继承类,必须先继承再实现

5、如果继承类和实现类中有同名的方法,则执行继承类中的方法

6、如果一个类实现了接口,必须重写接口中的所有抽象方法,否则该就变成抽象类

7.3、内置接口

7.3.1、cloneable

  • 克隆对象

7.3.2、Comparable

  • 内置比较,需要自己去指定比较规则

7.3.3、Comparator

  • 外置比较,需要自己去指定比较规则

使用:

  • 实现接口
  • 重写方法(多次使用多态)
  • 调用方法

8、内部类

  • 将一个类嵌套到另一个类中,可以解决java只允许单继承的弊端

1、外部类只能被public和缺省修饰,内部类可以被所有修饰符修饰

2、内部类中可以存在静态或者非静态资源

3、内部类中的非静态方法内,可以直接使用外部内的所有资源

​ 静态方法内,只能使用外部类中的静态资源

4、外部内使用内部类资源

​ 内部类非静态资源 -----> 创建内部类对象.资源名

​ 内部类静态资源 -------> 内部类名.资源名

5、打破了java单继承的限制,外部类和内部类都可以单独的继承其他类

6、在外部类使用内部类资源(和普通类调用对象一致)

​ 非静态资源 —> 创建内部类对象

​ 外部类名.内部类名 对象名 = new 外部类名().new 内部类名();

​ 内部类对象.资源名

​ 静态资源 -----> 内部类对象.静态资源名

7、内部类会产生独立的字节码文件(命名格式:外部类名$内部类名.class)

8、如果内部类和外部类有相同的非静态属性

​ this.属性名:调用内部类的资源

​ 外部类名.this.属性名:调用外部类的资源

8.1、成员内部类

8.1.1、普通成员内部类

  • 位于类中方法外,地位同等于普通成员变量

8.1.2、静态成员内部类

  • 类中方法外,有static修饰

注意:

  • 静态成员内部类可以直接使用外部类的静态资源
  • 非静态资源需要创建外部类对象来调用
  • 外部类可以使用内部类资源
  • 非静态资源 —> 内部类对象名.资源名
  • 静态资源 -----> 内部类名.资源名
  • 在外部类中直接使用内部类资源
  • 内部类静态资源 外部类名.内部类名.资源名
  • 内部类非静态资源 创建静态内部类对象
    • 外部类名.内部类名 对象名 = new 外部类名.内部类名();
    • 对象名.资源名
8.2、局部内部类
  • 位于类中方法类

注意:

1、局部内部类使用外部类资源是有所在方法决定的

​ 如果在普通方法内,那么可以直接使用外部类所有资源

​ 如果在静态方法内,那么只能直接使用外部类静态资源

2、局部内部类也会产生独立的字节码文件

​ 命令格式:外部类名$序号内部类名.class (序号从1开始)

3、局部内部类 使用了所在方法的局部变量,那么这个变量会默认被final修饰

8.3、匿名内部类
  • 是局部内部类的一种,是没有名字的局部内类
  • 方法简化代码

8.3.1、声明方式

  • new 普通类/抽象类(){}
  • new 接口(){}
interface Student{
    void eat();
}

public class Person {
    public static void main(String[] args) {
        Person person = new Person();
        person.show();


    }
    public void show(){
        //匿名内部类
        Student student=()->{
            System.out.println("bbb");
        };
        student.eat();
    }
    public void show(){
        //lombda表达式
        ((Student) () -> System.out.println("aaa")).eat();
    }
}

8.3.2、匿名内部类的意义

  • 创建了匿名子类
  • 完成了对该==匿名子类对象==的创建

8.3.3、应用

  • 父类类型 变量名 = 匿名子类对象
  • 当需要一个接口作为参数时,可以直接传入接口的匿名子类对象

9、代码块

  • 位于类中方法外
9.1、普通成员代码块
  • 可以给成员变量赋值
  • 可以将构造器中重复的逻辑放入代码块中

注意:

1、代码块优先与构造器执行

2、普通成员代码块只要创建一次对象,就会执行一次

3、代码有作用域访问

int i =10;
{
	int i = 21;
	System.out.println(i);//21
}
9.2、静态成员代码块
  • 有static修饰
  • 给类中的静态成员变量赋值
  • 进行初始化操作

注意:

1、无论对象创建几次,都只会执行一次

2、静态成员代码块会优先与普通成员代码块执行

3、静态成员代码块也有作用域范围限制

10、类的初始化

  • 目的:给静态成员变量赋值
10.1、类的初始化方法< clinit >
  • 静态成员变量显示赋值语句
  • 静态代码块内容

执行顺序和代码编写顺序有关

10.2、触发初始化的条件
  • 使用了类中的静态资源(属性和方法)
  • 创建对象

注意:

1、类的初始化只会执行一次

2、如果有继承关系,会优先对父类进行初始化,然后再对子类进行初始化

3、父类和子类进行初始化时,会公用一个< clinit > 方法

4、子类使用了从父类经常的静态资源,那么只会先对父类进行初始化,不会导致子类进行初始化

11、类的实例化

  • 给类中实例成员变量赋值的过程
11.1、执行顺序

:
1 super()
2 实例变量显示赋值语句
3 代码块内容
4 构造器内

混合初始化:
先父再子
先类再实例

public class Person {
    {
        System.out.println("1");
    }
    static {
        System.out.println(2);
    }
    String name = getName();

    public String getName() {
        System.out.println(4);
        return null;
    }
    static int age = getAge();

    public static int getAge(){
        System.out.println(5);
        return 10;
    }
    Person(){
        super();
        System.out.println(3);
    }
}
public class Student extends Person{
    {
        System.out.println("6");
    }
    static {
        System.out.println(7);
    }
    String name = getName();

    public String getName() {
        System.out.println(8);
        return null;
    }
    static int age = getAge();

    public static int getAge(){
        System.out.println(9);
        return 10;
    }
    Student(){
        super();
        System.out.println("A");
    }
}
@Test
public void test01(){
    new Student();
	//2 5 7 9 1 8 3 6 8 A
}
/*
1、先进入构造器调用super先父类先进行实例化,
2、先进行父类的静态内容(静态代码块和静态变量和方法)初始化,再进行子类静态内容初始化,
3、再进行父类代码块进行初始化,进行子类代码块初始化
4、执行父类方法初始化,如果子类重写了父类的方法,就执行子类重写的方法进行初始化,
5、执行父类构造器初始化,最后执行子类构造器初始化
*/

12、枚举

12.1、枚举的方法
枚举对象.ordinal()
枚举对象.name()
枚举类型.values()
枚举类型.valueOf();
12.2、枚举使用注意点

1.自定义枚举类型 默认继承自Enum类

2.枚举类中的属性值必须在枚举对象的下面

3.编译器生成 values() valueOf();

4.枚举类可以实现接口 既可以统一处理 也可以每一个枚举对象单独处理

5.枚举可以在switch中使用

4、异常

  • 在程序执行过程中发生的问题(bug)

4.1、异常的体系

  • Throwable

    • error : 程序员无法解决的问题,eg:堆内存溢出

    • exception:为编译时异常

      • 编译时异常 : 程序写完就报错,eg:io异常-- > 文件未找到异常
      • 运行时异常 : 程序运行才发生的异常

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Nf7XYGcm-1692363825584)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20230729095452126.png)]

4.2、异常发生后默认的处理方法

1、产生异常对象 发生异常的位置会产生异常对象

2、如果没有对异常对象处理 则会沿着调用关系逐级向上抛出,直到抛到jvm

3、抛到jvm后会终止程序运行,并打印异常信息

​ 异常信息:异常类型,触发异常的原因,异常抛出的位置(轨迹,从哪开始抛出)

4.3、异常的处理

4.3.1、throws
  • 在方法声明处,声明异常
public static void main(String[] args) throws InvalidValueException {
}
4.3.2、throw
  • 在可能出现异常的位置手动抛出可能要出的异常
if (line <= 0 || column <= 0){
    throw new InvalidValueException("矩形的长宽必须是正整数");
}
4.3.3、try{}catch{}
  • 通过try{}catch{}来捕获异常
try{
    //可能发生异常的代码
} catch 异常类型 异常名{
    //对异常的处理
} finally {
    //无论是否发生异常,都会执行
    //如果不想执行finally,可以使用System.exit(0)  0:正常退出,非0:非正常退出----> 强制		退出jvm
}

注意:

1、只能捕获catch中声明的异常类型

2、可以进行多重catch()

3、进行多重异常时,父类在后,子类在前

4、try中声明的变量,只能在try的语句块内使用,如果想要在其他位置使用需要进行作用域提示,还需要注意初始化问题

4.4、自定义异常

  • 先继承所需的异常,再创建一个无参,一个String参数的构造器,要使用是就new 异常类名
public class InvalidValueException extends Exception{
    public InvalidValueException(){
    }
    public InvalidValueException(String message) {
        super(message);
    }
}

5、字符串

5.1、相同点和区别

  • jdk8及之前,底层都采用char[] 存贮,之后采用byte[]存储
长度 线程安全 效率
String 不可变 不安全
StringBuilder 可变 不安全
StringBuffer 可变 安全 中(待定)

5.1.1、创建字符串对象

1、字面量

2、构造器

3、拼接

4、方法

5.2、String

5.2.1、特点

1、位于java.lang包下,不需要导包

2、在java内,只要是使用双引号包裹起来的都可以看错String类型

3、String类的对象是不可变对象,不能修改原始值

4、数据存储在字符串常量池内,相同的数据只会开辟一块内存空间

5、底层被final修饰,所以无法修改

6、实现了Comparable接口,可以进行字符串比较

5.2.2、常用方法
.isEmpty()  //判断字符串是否为空
.length() //获取字符串长度
.toUpperCase() //将小写字母换成大写
.toLowerCase() //将大写字母换成小写
.equals(param) //比较字符串内容是否相同
.equalsIgnoreCase(param) //忽略大小写比较字符串内容
.compareTo(param) //按照下标进行比较,逐个字符比较ASCII码值,
.compareToIgnoreCase(parm) //忽略大小写比较大小
.concat(param) //把param拼接到字符串后
.trim() //去空格 
.contains(param) //查看字符串是否包含某些连续字符 
.indexOf(param) //获取指定元素第一次出现的下标(没有就返回-1)
.lastIndexOf(param) //获取指定元素最后一次出现的下标(没有就返回-1)
.chatAt(param) //获取指定下标的字符
.toCharArray() //将字符串变为char数组
.substring(param1,param2) //一个参数时,从参数一直截取到最后,两个参数就是从param1截取到param2,但不包括怕param2
.startsWith(param) //判断是否以指定内容开始
.startsEnd(param)  //判断是否一指定内容结束
.replace(param1,param2) //将字符串中的param1内容换成param2
.split(param) //将字符串切割为几部分,以param为切割点,
//获取字符串中字符出现的次数
//方式1
public void test01(){
    String str = "abfffafdasfgtwdds";
    char c[] = str.toCharArray();
    Arrays.sort(c);
    String s = new String(c);
    System.out.println("s = " + s);
    for (int start = 0; start <s.length();) {
        char at = s.charAt(start);
        int i = s.lastIndexOf(at);
        System.out.println(at+" ------> " + (i-start+1));
        start = i +1;

    }
}
//方式2
@Test
public void test01(){
    String s = "ABCDABCDABCDAB";

    //todo 1.将不重复字符组成字符串 ABCD
    //1.1创建一个空字符串用于存储不重复的字符
    String newStr = "";
    //1.2遍历旧的字符串将不重复字符添加到新的字符串内
    for (int i = 0; i <s.length() ; i++) {
        //获取指定下标的字符
        char e = s.charAt(i);
        //如果没有出现过 进行添加
        if(newStr.indexOf(e)==-1){
            newStr+=e;
        }
    }
   // System.out.println("newStr = " + newStr);//ABCD
    //todo 2.再让不重复字符串内的每一个字符与原始字符串进行比较
    for(int i = 0;i<newStr.length();i++){
        //定义变量统计数量
        int count = 0;

        char nc = newStr.charAt(i);

        for(int j = 0;j<s.length();j++){

            char oc = s.charAt(j);
            if(nc==oc){
                count++;
            }
        }
        System.out.println(nc+" --> "+count);
    }
}
5.2.3、内存图

5.3、StringBuilder

  • 1.0就有了

  • 和StringBuffer一样,通过传入的参数来返回底层代码的coder的值来判断传入的数据类型来决定字符串的长度

5.3.1、方法
.setlength() //设置长度
.append(param)  //在字符串后拼接内容
.delete(param1,param2) //删除范围(左闭右开)
.insert(param1,param2) //在下标param1之前插入param2内容
.replace(param1.param2,param3) //把下标param1~param2直接的内容替换成param3
.reverse() //反转字符串

5.4、StringBuffer

  • 1.5才出现
  • 方法和StringBuilder通用

6、API

6.1、Array

  • static int binarySearch(int[] a, int key) :要求数组有序,在数组中查找key是否存在,如果存在返回第一次找到的下标,不存在返回负数

  • static int[] copyOf(int[] original, int newLength) :根据original原数组复制一个长度为newLength的新数组,并返回新数组

  • static int[] copyOfRange(int[] original, int from, int to) :复制original原数组的[from,to)构成新数组,并返回新数组

  • static boolean equals(int[] a, int[] a2) :比较两个数组的长度、元素是否完全相同

  • static void fill(int[] a, int val) :用val填充整个a数组

  • static void fill(int[] a, int fromIndex, int toIndex, int val):将a数组[fromIndex,toIndex)部分填充为val

  • static void sort(int[] a) :将a数组按照从小到大进行排序

  • static void sort(int[] a, int fromIndex, int toIndex) :将a数组的[fromIndex, toIndex)部分按照升序排列

  • static String toString(int[] a) :把a数组的元素,拼接为一个字符串,形式为:[param1,param2…]

6.2、包装类

基本数据类型 包装类(java.lang包) 缓存对象
byte Byte -128~127
short Short -128~127
int Integer -128~127
long Long -128~127
float Float 没有
double Double 没有
char Character 0~127
boolean Boolean true和false
6.2.1、装箱
  • 将基本数据类型转为包装类型
//手动装箱
int i = 10;
Integer inte = Integer.valueOf(i)

//自动装箱(底层包装类对象自己去调.valueOf())
int i = 10;
Integer inte = i;
6.2.2、拆箱
  • 把包装类型转为基本数据类型
//手动拆箱
Integer inte = new Integer(1021);
int i = inte.intvalue()
//自动拆箱(底层包装类对象自己去调用自己类型的Value()--->intVaule(),doubleValue() .......)
Integer inte = new Integer(1021);
int i = inte;
6.2.3、String和基本数据类型的转换
  • 基本数据类型------> 字符串

1、字符串拼接

2、String.valueOf(基本数据类型)

  • 字符串 -----------> 基本数据类型

包装类型.parse基本数据类型(字符串对象) Integer.parseInt(s)

6.2.4、包装类的方法
.MAX_VALUE  //获取包装类能存储的最大数值
.MIN_VALUE  //获取包装类能存储的最小数值
.max(param1,param2) //获取两数最大值
.min(param1,param2) //获取两数最小值
.sum(param1,param2) //求两数和
.compare(param1,param2) //判断两数大小
.toBinaryString(param)  //十进制转换为2进制,
6.2.5、问题

1、赋值问题

2、缓存问题

3、不可以变对象

6.3、数学类

.abs()  //绝对值
.sqrt() //开平方
.pow(param1,param2) //param1的param2次方
.floor(param)  //向下取整
.ceil(param)  //向上取整
.random()  //随机数
.round(param) //四舍五入
.max(param1,param2) //比较大小
6.3.1、BigInteger
  • 不可变的任意精度的整数。
	@Test
	public void test01(){
	//	long bigNum = 123456789123456789123456789L;
	BigInteger b1 = new BigInteger("123456789123456789123456789");
	BigInteger b2 = new BigInteger("78923456789123456789123456789");
	//System.out.println("和:" + (b1+b2));//错误的,无法直接使用+进行求和
	System.out.println("和:" + b1.add(b2));
	System.out.println("减:" + b1.subtract(b2));
	System.out.println("乘:" + b1.multiply(b2));
	System.out.println("除:" + b2.divide(b1));
	System.out.println("余:" + b2.remainder(b1));
}
6.3.2、BigDecimal
  • 不可变的、任意精度的有符号十进制数。
@Test
	public void test02(){
		/*double big = 12.123456789123456789123456789;
		System.out.println("big = " + big);*/
		
		BigDecimal b1 = new BigDecimal("123.45678912345678912345678912345678");
		BigDecimal b2 = new BigDecimal("7.8923456789123456789123456789998898888");
		
		//System.out.println("和:" + (b1+b2));//错误的,无法直接使用+进行求和
		System.out.println("和:" + b1.add(b2));
		System.out.println("减:" + b1.subtract(b2));
		System.out.println("乘:" + b1.multiply(b2));
		System.out.println("除:" + b1.divide(b2,20,RoundingMode.UP));
        //divide(BigDecimal divisor, int scale, int roundingMode)
		System.out.println("除:" + b1.divide(b2,20,RoundingMode.DOWN));
        //divide(BigDecimal divisor, int scale, int roundingMode)
		System.out.println("余:" + b1.remainder(b2));
	}
	//保留两位小数的方式:
	@Test
	public void test02(){
        double f = 111231.5585;
		BigDecimal bg = new BigDecimal(f);
		double f1 = bg.setScale(2, BigDecimal.ROUND_HALF_UP).doubleValue();
		System.out.println(f1);
    }

6.4、时间类

6.4.1、java.util.Date
  • new Date():当前系统时间
  • long getTime():返回该日期时间对象距离1970-1-1 0.0.0 0毫秒之间的毫秒值
  • new Date(long 毫秒):把该毫秒值换算成日期时间对象
  • .localeString() :将日期转为字符串形式
@Test
public void test5(){
    long time = Long.MAX_VALUE;
    Date d = new Date(time);
    System.out.println(d);
}

@Test
public void test4(){
    long time = 1559807047979L;
    Date d = new Date(time);
    System.out.println(d);
}
@Test
public void test3(){
    Date d = new Date();
    long time = d.getTime();//获取当前时间的毫秒值
    System.out.println(time);//1559807047979
}

@Test
public void test2(){
    long time = System.currentTimeMillis();
    System.out.println(time);//1559806982971
    //当前系统时间距离1970-1-1 0:0:0 0毫秒的时间差,毫秒为单位
}

@Test
public void test1(){
    Date d = new Date();
    System.out.println(d);
}
6.4.2、java.text.SimpleDateFormat
  • 用于日期格式化
@Test
	public void test10() throws ParseException{
		String str = "2023年10月21日 16时03分14秒 545毫秒  星期六 +0800";
		SimpleDateFormat sf = 
            new SimpleDateFormat("yyyy年MM月dd日 HH时mm分ss秒 SSS毫秒  E Z");
		Date d = sf.parse(str);
		System.out.println(d);
	}
	
	@Test
	public void test9(){
		Date d = new Date();

		SimpleDateFormat sf = 
            new SimpleDateFormat("yyyy年MM月dd日 HH时mm分ss秒 SSS毫秒  E Z");
		//把Date日期转成字符串,按照指定的格式转
		String str = sf.format(d);
		System.out.println(str);
	}
6.4.3、DateTimeFormatter:日期时间格式化

该类提供了三种格式化方法:

  • 预定义的标准格式。如:DateTimeFormatter.ISO_DATE_TIME; ISO_DATE

  • 本地化相关的格式。如:DateTimeFormatter.ofLocalizedDate(FormatStyle.MEDIUM)

  • 自定义的格式。如:DateTimeFormatter.ofPattern(“yyyy-MM-dd hh:mm:ss”)

@Test
	public void test(){
		LocalDateTime now = LocalDateTime.now();
		//预定义的标准格式
		DateTimeFormatter df = DateTimeFormatter.ISO_DATE_TIME;//2021-10-21T10:21:23.211
        //格式化操作
		String str = df.format(now);
		System.out.println(str);
	}

	@Test
	public void test1(){
		LocalDateTime now = LocalDateTime.now();
		//本地化相关的格式
		//DateTimeFormatter df = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.LONG);//2021年10月21日 上午10时21分03秒
		DateTimeFormatter df = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.SHORT);//21-10-21 下午10:21
        //格式化操作
		String str = df.format(now);
		System.out.println(str);
	}

	@Test
	public void test2(){
		LocalDateTime now = LocalDateTime.now();
		//自定义的格式
		DateTimeFormatter df = DateTimeFormatter.ofPattern("yyyy年MM月dd日 HH时mm分ss秒  SSS毫秒  E 是这一年的D天");
         //格式化操作
		String str = df.format(now);
		System.out.println(str);
	}

	//把字符串解析为日期对象
    public void test3(){
        //自定义的格式
        DateTimeFormatter pattern = DateTimeFormatter.ofPattern("yyyy.MM.dd");
        //解析操作
        LocalDate parse = LocalDate.parse("2021.12.12", pattern);
        System.out.println(parse);
    }
6.4.4、jdk1.8后的日期类
  • LocalDate、LocalTime、LocalDateTime
方法 描述
now() / now(ZoneId zone) 静态方法,根据当前时间创建对象/指定时区的对象
of() 静态方法,根据指定日期/时间创建对象
getDayOfMonth()/getDayOfYear() 获得月份天数(1-31) /获得年份天数(1-366)
getDayOfWeek() 获得星期几(返回一个 DayOfWeek 枚举值)
getMonth() 获得月份, 返回一个 Month 枚举值
getMonthValue() / getYear() 获得月份(1-12) /获得年份
getHours()/getMinute()/getSecond() 获得当前对象对应的小时、分钟、秒
withDayOfMonth()/withDayOfYear()/withMonth()/withYear() 将月份天数、年份天数、月份、年份修改为指定的值并返回新的对象
with(TemporalAdjuster t) 将当前日期时间设置为校对器指定的日期时间
plusDays(), plusWeeks(), plusMonths(), plusYears(),plusHours() 向当前对象添加几天、几周、几个月、几年、几小时
minusMonths() / minusWeeks()/minusDays()/minusYears()/minusHours() 从当前对象减去几月、几周、几天、几年、几小时
plus(TemporalAmount t)/minus(TemporalAmount t) 添加或减少一个 Duration 或 Period
isBefore()/isAfter() 比较两个 LocalDate
isLeapYear() 判断是否是闰年(在LocalDate类中声明)
format(DateTimeFormatter t) 格式化本地日期、时间,返回一个字符串
parse(Charsequence text) 将指定格式的字符串解析为日期、时间

6.5、系统类

  • static long currentTimeMillis() :返回当前系统时间距离1970-1-1 0:0:0的毫秒值

  • static void exit(int status) :退出当前系统

  • static void gc() :运行垃圾回收器。

  • static String getProperty(String key):获取某个系统属性

7、集合

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wC6arvQc-1692363825585)(F:\Typora\images\JavaSE\image-20230804183012783.png)]

7.1、Collection

  • 集合是java中提供的一种容器,可以用来存储多个数据

集合和数组既然都是容器,它们有啥区别呢?

数组的长度是固定的。集合的长度是可变的。

数组中可以存储基本数据类型值,也可以存储对象,而集合中只能存储对象

//1、添加元素**

add(E obj)//添加元素对象到当前集合中
addAll(Collection<? extends E> other)//添加other集合中的所有元素对象到当前集合中,即this = this ∪ other

//2、删除元素**
    
boolean remove(Object obj)//从当前集合中删除第一个找到的与obj对象equals返回true的元素。
boolean removeAll(Collection<?> coll)//从当前集合中删除所有与coll集合中相同的元素。即this = this - this ∩ coll
boolean retainAll(Collection<?> coll)//从当前集合中删除两个集合中不同的元素,使得当前集合仅保留与c集合中的元素相同的元素,即当前集合中仅保留两个集合的交集;

//3、查询与获取元素**
    
boolean isEmpty()//判断当前集合是否为空集合。
boolean contains(Object obj)//判断当前集合中是否存在一个与obj对象equals返回true的元素。
boolean containsAll(Collection<?> c)//判断c集合中的元素是否在当前集合中都存在。即c集合是否是当前集合的“子集”。
int size()//获取当前集合中实际存储的元素个数
Object[] toArray()//返回包含当前集合中所有元素的数组
Iterator<Integer> iterator = collection.iterator();
while (iterator.hasNext()){
    Integer next = iterator.next();
    if (next%10 ==3){
        iterator.remove(); //遍历时必须使用迭代器对象删除,不能使用集合对象删除
    } else {
        System.out.print(next + " ");
    }

}
7.1.1、List
  • 有序(添加顺序),不唯一
  • 有四种遍历方法
    • 普通for
    • 增强for
    • 迭代器
    • List迭代器
@Test
public void test02(){
    Collection<String> c = new ArrayList<>();
    c.add("张三");
    c.add("李四");
    c.add("王五");
    c.add("赵六");
	//增强for循环(本质也是迭代器)
    for (String s : c) {
        System.out.println("s = " + s);
    }
    Iterator<String> iterator = c.iterator();
	//迭代器
    while (iterator.hasNext()){
        String ele = iterator.next();
        System.out.println("ele = " + ele);
    }
}
a、ArrayList
  • 底层是数组,增删慢,查询快
b、Vector
  • 底层是数组,增删慢,查询快,线程安全
c、LinkedList
  • 底层是双向链表,增删快,删除慢(需要遍历所有元素去查找要删除的元素)
ArrayList 和 Vector比较

相同:

​ 1、底层都是数组

​ 2、都继承了AbstractList类

不同:

​ 1、初始化长度时间不同:

​ ArrayList第一次创建对象是,数组长度为0,第一次添加数据是,数组长度为10

​ Vector第一次创建对象是,数组长度就为10

​ 2、扩容方式不同

​ ArrayList扩容0.5倍

​ Vector看底层的capacityIncrement=0是,扩容到2倍(为旧数组长度+旧数组长度)

​ capacityIncrement>0是,为(capacityIncrement+就数组长度)

​ 3、线程安全和效率不同

​ ArrayList线程不安全,效率高

​ Vector线程安全,效率低

7.1.2、Set
  • 无序,不唯一
  • 和Collection的方法一直,没有新增的方法
  • 两种遍历方法
    • 增强for
    • 迭代器
a、HashSet
  • 无序,唯一

  • 底层使用HashMap的key去存储(因为key是唯一的)

b、LinkedHashSet
  • 有序(插入顺序),唯一

  • 底层使用LinkedHashMap的key去存储

c、TreeSet
  • 有序(自然排序),唯一
  • 底层使用TreeMap的key去存储

7.2、Map

  • 存储键值对,key唯一
  • 两种遍历方法
    • 可以使用keySet()获取key,在使用get(key)方法获取value进行遍历
    • 使用entrySet()获取Set对象,通过Set的迭代器进行遍历
//方式一
Set<String> keys = map.keySet();
for (String key : keys) {

    String value = map.get(key);

    System.out.println(key+","+value);
}

//方式二
Set<Map.Entry<Integer, String>> entrySet = map.entrySet();
Iterator<Map.Entry<Integer, String>> iterator = entrySet.iterator();

while (iterator.hasNext()){
    Map.Entry<Integer, String> entry = iterator.next();
    Integer key = entry.getKey();
    String value = entry.getValue();
    System.out.println(key+" "+value);
}
7.2.1、HashMap
  • key无序,不重复

  • 底层采用数组加链表存储,当节点数量大于8时,且数组长度不小于64时,采用数组加红黑树

7.2.2、LinkedHashMap
  • 底层双向链表加哈希表

  • key有序(添加顺序),唯一

  • 如果存储自定义类型数据 必须要重新 hashCode() equals()

7.2.3、TreeMap
  • 底层哈希表加红黑树

  • key有序(自然排序),唯一

  • 线程安全

7.2.4、Hashtable
  • 跟HashMap一样,只不过线程安全
HashMap vs Hashtable比较

相同:

​ 1、都实现了Map接口

​ 2、底层都是用了哈希表

不同:

​ 1、对null值的处理

​ HashMap的key和value都可以为null

​ Hashtable的key和value都不能为null

​ 2、底层数组开辟空间的时间不同

​ HashMap在第一添加数据时开辟空间,数组长度为16

​ Hashtable在创建对象时开辟空间,数组长度为11

​ 3、线程安全和效率

​ HashMap线程不安全,效率高

​ Hashtable线程安全,效率低

8、泛型

定义:参数化类型

  • 优点:

1、输入输入的类型

2、减少类型的转换

  • 泛型的位置:
    类: 定义在类上 泛型类型
    接口: 泛型接口
    方法:
    泛型方法:方法执行时才确定泛型类型的方法

  • 自定义泛型方法:
    将泛型的声明放到返回值前
    public void cc(){}
    public static void show(){}

  • 定义泛型的上限:
    T extends 类型:
    T 未知类型 可以是指定类型也可以是它的孩子
    T extends Animal: T 既可以是Animal 也可以是Animal的孩子

    T extends Number & Comparable:指定多个上限

  • 泛型的擦除:
    1.需要泛型却没有指定泛型则采用最高类型Object
    2.泛型存在于编译时

  • 泛型的通配符:
    ?: 代表未知类型
    通配符的下限: ? super T 最小类型
    ? super Dog : 未知类型可以是Dog 也可以是Dog的父亲
    通配符的上限: ? extends T 最高类型
    ? extends Comparable: 未知类型可以是Comparable 也可以是Comparable的孩子

9、IO流

  • 基本流:
    • 字节流
      • 字节输入流:InputStream
      • 字节输出流:OutputStream
    • 字节流
      • 字节输入流:Reader
      • 字节输出流:Writer

9.1、操作文件的基本方法

getName()	//获取文件名
length()	//获取文件大小(不能获取文件夹大小,要获取文件夹大小就获取文件夹中所有文件大小的和)
lastModified()	//获取文件最后修改日期(返回毫秒值)
.isFile()	//是否为文件
isDirectory()	//是否为文件夹
exists()	//是否存在
.createNewFile()	//创建文件
.delete()	//删除文件(只能删除空文件夹,直接删除,不放入回收站,但不能删除文件夹)
.mkdir()	//创建文件夹
.mkdirs()	//创建多级文件夹

9.2、节点流

  • 作用在对象上的流

9.3、处理流

  • 作用在流上的流
9.3.1、缓冲流
  • 字节缓冲流 byte[8192]
    • BufferedInputStream
    • BufferedOutputStream
  • 字节缓冲流char[8192]
    • BufferedReader
    • BufferedWriter
9.3.2、转换流
  • InputStreamReader:字节输入流----->字符输入流
  • OutputStreamWriter:字节输出流----->字符输出流
9.3.3、对象流
  • 对象输入流:ObjectInputStream
  • 对象输出流:ObjectOutputStream

注意:
1.NotSerializableException:没有序列化异常
需要存储对象所在的类 必须实现 Serializable 接口
2.如果某些属性不想被序列化
属性前+ transient
属性前+ static
3.序列化和反序列化版本号要一致

4.可以使用对象流写出基本类型的数据
读取顺序要和写出顺序一致

9.3.4、打印流
  • PrintStream
  • printWriter
9.3.5、标准输入输出流
  • System.in
  • System.err
  • System.out

9.4、IOUtils

  • 导包
<dependency>
    <groupId>commons-iogroupId>
    <artifactId>commons-ioartifactId>
    <version>2.11.0version>
dependency>

10、多线程

10.1、线程的创建

10.1.1、继承Thread

1、实现Thread类

2、重新run方法

3、创建Thread对象,调用start方法

public class CreateThread extends Thread{
    @Override
    public void run() {
        while (true){
            System.out.println("thread run...................");
        }
    }
}

public static void main(String[] args) {
    CreateThread createThread = new CreateThread();
    createThread.start();
}
//匿名类创建
new Thread(){
    @Override
    public void run() {
        System.out.println("Thread.............");
    }
}.start();
10.1.2、实现Runnable接口

1、实现Runnable接口

2、重新run方法

3、创建实现了Runnable接口类的对象

4、创建Thread对象,传入实现了Runnable接口类的对象

5、调用start方法

public class CreateRunnable implements Runnable{
    @Override
    public void run() {
        while (true){
            System.out.println("runnable run................");
        }
    }
}

public static void main(String[] args) {
   new Thread(new CreateRunnable()).start();
}
//匿名类创建
new Thread(new Runnable() {
    @Override
    public void run() {
        System.out.println("runnable..........");
    }
}).start();
10.1.3、实现callable接口

1、实现Callable接口,可以使用泛型

2、重写call方法

3、创建FutureTask对象,传入Callable对象

4、创建Thread对象,传入FuturTask对象

5、调用Thread对象的start方法

public class CreateCallable implements Callable<String> {
    @Override
    public String call() throws Exception {
        System.out.println("Callable实现多线程");
        return "Callable实现多线程";
    }
}

public static void main(String[] args) {
    FutureTask futureTask = new FutureTask<>(new CreateCallable());
    new Thread(futureTask).start();
}
10.1.4、线程池
//固定大小的线程池
ExecutorService fixedThreadPool = Executors.newFixedThreadPool(5);
fixedThreadPool.execute(() -> System.out.println("Hello, world!"));
fixedThreadPool.shutdown(); // 必须调用 shutdown() 才能关闭线程池。否则会导致内存泄漏。


//可缓存的线程池
ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
cachedThreadPool.execute(() -> System.out.println("Hello, world!"));
cachedThreadPool.shutdown(); // 必须调用 shutdown() 才能关闭线程池。否则会导致内存泄漏。

//单个线程的线池
ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor();
singleThreadExecutor.execute(() -> System.out.println("Hello, world!"));
singleThreadExecutor.shutdown(); // 必须调用 shutdown() 才能关闭线程池。否则会导致内存泄漏。


//可执行延迟任务的线程池
ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(2);
scheduledThreadPool.scheduleAtFixedRate(() -> System.out.println("Hello, world!"), 0, 5, TimeUnit.SECONDS);
scheduledThreadPool.shutdown(); // 必须调用 shutdown() 才能关闭线程池。否则会导致内存泄漏。

10.2、常用方法

10.2.1 构造方法
  • public Thread() :分配一个新的线程对象。
  • public Thread(String name) :分配一个指定名字的新的线程对象。
  • public Thread(Runnable target) :分配一个带有指定目标新的线程对象。
  • public Thread(Runnable target,String name) :分配一个带有指定目标新的线程对象并指定名字。
10.2.2 线程使用基础方法
  • public void run() :此线程要执行的任务在此处定义代码。

  • public String getName() :获取当前线程名称。

  • public static Thread currentThread() :返回对当前正在执行的线程对象的引用。

  • public final int getPriority() :返回线程优先级

  • public final void setPriority(int newPriority) :改变线程的优先级

    • 每个线程都有一定的优先级,优先级高的线程将获得较多的执行机会。每个线程默认的优先级都与创建它的父线程具有相同的优先级。Thread类提供了setPriority(int newPriority)和getPriority()方法类设置和获取线程的优先级,其中setPriority方法需要一个整数,并且范围在[1,10]之间,通常推荐设置Thread类的三个优先级常量:
    • MAX_PRIORITY(10):最高优先级
    • MIN _PRIORITY (1):最低优先级
    • NORM_PRIORITY (5):普通优先级,默认情况下main线程具有普通优先级。
public static void main(String[] args) {
    Thread t = new Thread(){
        public void run(){
            System.out.println(getName() + "的优先级:" + getPriority());
        }
    };
    t.setPriority(Thread.MAX_PRIORITY);
    t.start();

    System.out.println(Thread.currentThread().getName() +"的优先级:" + Thread.currentThread().getPriority());
}
10.2.3 线程控制常见方法
  • public void start() :导致此线程开始执行; Java虚拟机调用此线程的run方法。

  • public static void sleep(long millis) :线程睡眠,使当前正在执行的线程以指定的毫秒数暂停(暂时停止执行)。

  • void join() :加入线程,当前线程中加入一个新线程,等待加入的线程终止后再继续执行当前线程。

    void join(long millis) :等待该线程终止的时间最长为 millis 毫秒。如果millis时间到,将不再等待。

    void join(long millis, int nanos) :等待该线程终止的时间最长为 millis 毫秒 + nanos 纳秒。

  • public final void stop():强迫线程停止执行。 该方法具有不安全性,已被弃用,最好不要使用。

    • 调用 stop() 方法会立刻停止 run() 方法中剩余的全部工作,包括在 catch 或 finally 语句中的,并抛出ThreadDeath异常(通常情况下此异常不需要显示的捕获),因此可能会导致一些清理性的工作的得不到完成,如文件,数据库等的关闭。
    • 调用 stop() 方法会立即释放该线程所持有的所有的锁,导致数据得不到同步,出现数据不一致的问题。
  • public void interrupt():中断线程,实际上是给线程打上一个中断的标记,并不会真正使线程停止执行。

  • public static boolean interrupted():检查线程的中断状态,调用此方法会清除中断状态(标记)。

  • public boolean isInterrupted():检查线程中断状态,不会清除中断状态(标记)

10.3、线程安全问题

10.3.1、同步方法
public synchronized void method(){ 
}

同步方法的锁对象

​ 普通同步方法:this

​ 静态普通同步方法:类型.class

//普通同步方法
public synchronized void a(){}
//静态普通同步方法
public synchronized static  void b(){}
10.3.2、同步代码块
  • 注意: 一个线程进入了同步代码块 那么其他线程无法进入使用同一个同步监视器对象的其他同步代码块
synchronized(同步锁){
}

10.4、线程通信

  • 主要通过线程间的等待和唤醒机制实现

wait():等待,会释放锁

notify():唤醒等待的线程

notifyAll():唤醒所有的等待的线程

线程通信的方法只能在同步代码块或同步方法中使用

10.5、线程生命周期

  • 新建(New)

当一个Thread类或其子类的对象被声明并创建时,新生的线程对象处于新建状态

  • 就绪(Runnable)

当线程对象调用start()方法后,线程就从新建状态转为就绪状态

  • 运行(Running)

如果就绪的线程获得了CPU资源,开始执行run()方法的线程,则该线程处于运行状态

  • 阻塞(Blocked)

当运行的线程遇到某些特殊的情况导致线程临时放弃CPU的资源,不在继续执行,此时线程进入阻塞状态

  • 死亡(Dead)

线程完成任务或者意外终止后,线程处于死亡状态

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-lbfBMRx7-1692363825585)(F:\Typora\images\JavaSE\image-20230808095550958.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kCkVNRHx-1692363825585)(F:\Typora\images\JavaSE\image-20230809095640422.png)]

10.6、释放锁和死锁

10.6.1、释放锁
  • 执行完毕,释放锁
  • 发生异常,会释放锁
  • wait(),会释放锁
10.6.2、死锁
  • 互相持有对方锁资源,不释放

10.7、sleep和wait的区别

  • sleep不会释放锁,wait会释放锁

  • sleep可指定休眠时间,时间结束后将自动继续执行,wait需要可以指定时间也可以无限等待,直到notify或者notifyALll

  • sleep在Thread类中,是静态方法,wait属于Object类中

    ​ 调用wait方法是由锁对象调用,而锁对象的类型是任意类型的对象

11、网络编程

11.1、软件架构

  • C/S结构:客户端/服务器架构
  • B/S架构:浏览器/服务器架构

11.2、网络通信协议

  • 通过计算机网络可以使多台计算机实现连接,位于同一个网络中的计算机在进行连接和通信时需要遵守一定的规则,这些连接和通信的规则就被称为网络通信协议,它对数据的传输格式、传输速率、传输步骤等做了一定的统一规定,通信双方必须同时遵守才能完成数据交换
TCP/IP

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ejBd8z6a-1692363825585)(F:\Typora\images\JavaSE\image-20230808110021342.png)]

  • 链路层:定义物理传输通道。通过是对某些网络连接设备的驱动协议,例如网线、光纤提供的驱动
  • 网络层:是TCP/IP协议的核心,将传输数据进行分组,将分组数据发送到目标计算机或者网络
  • 传输层:主要使用网络程序进行通信,在进行网络通信时,可以采用TCP协议,也可以采用UDP协议。TCP(Transmission Control Protocol)协议,即传输控制协议,是一种面向连接的、可靠的、基于字节流的传输层通信协议。UDP(User Datagram Protocol,用户数据报协议):是一个无连接的传输层协议、提供面向事务的简单不可靠的信息传送服务。
  • 应用层:主要负责应用成俗的协议,例如:HTTP协议
TCP/UDP
  • UDP:用户数据协议

    • 非面向连接的,不可靠的
    • 大小限制的
    • 数据报
  • TCP

    • 面向连接的,可靠的
三次握手
  • TCP协议中,在==发送数据的准备阶段==,客户端与服务器之间的三次交互,以确保连接的可靠
    • 第一次握手:客户端向服务器发送连接请求,等待服务器确认
    • 第二次握手:服务器向客户端发送一个响应,通知客户端收到了连接请求
    • 第三次握手:客户端再次向服务器端发送确认信息,确认连接
四次挥手
  • TCP协议中,在发送数据结束后,释放连接时需要进行四次挥手
    • 第一次挥手:客户端向服务器端提出结束连接
    • 第二次挥手:服务器端收到客户端释放连接的请求后,会将最后的数据发给客户端,并告知上层的应用进程不再接收数据
    • 第三次挥手:服务器发送完数据后,会给客户端发送一个释放连接的报文,那么客户端接收后就知道可以正式释放连接了
    • 第四次挥手:客户端收到服务器端最后发送的释放连接的报文后,要回复一个彻底断开的报文,这样服务器收到后才能彻底释放连接。这里客户端,发送完最后的报文后,会等待2MSL,因为有可能服务器没有收到最后的报文,如果服务器迟迟没有收到,那么会再次给客户端发送释放连接的报文,客户端在等待时间范围内收到,就重新发送最后的报文,并重新计时。如果等待@2MSL后还没有收到,就彻底断开连接

12、反射

12.1、类加载

12.1.1、类加载的过程

a、加载 load

  • 将指定类型的class字节码数据读入内存

b、链接 link

  • 验证:确保被加载的类的正确性
    • 格式验证
    • 语义检查
    • 字节码验证
  • 准备:准备对象的内存(方法区)
  • 解析:将类、接口等的符号引用转为直接【地址】引用

c、初始化 initialize

  • 执行==<clinit>==类初始化方法
12.1.2、类的初始化

a、那些操作会导致类的初始化

  • 运行主方法所在的类,要先完成类初始化,在执行main方法
  • 创建对象
  • 调用类中的静态属性
  • 子类初始化是,发现父类没有初始化,会先初始化父类
  • 通过反射操作某个类时,这个类没有初始化,就会导致该类先初始化
12.1.3、类加载器

a、引导类加载器(Bootstrap Classloader) 又称为根类加载器

jdk8前
    它负责加载jre/rt.jar核心库
    它本身不是Java代码实现的,也不是ClassLoader的子类,获取它的对象时往往返回null
jdk8后
    "jdk.management.jfr" 
    "java.rmi"
    "java.logging" 
    "java.xml"
    "jdk.jfr" 
    "java.datatransfer"
    "jdk.net" 
    "java.naming"
    "java.desktop" 
    "java.prefs" 
    "java.security.sasl"
    "jdk.naming.rmi"
    "java.base" 
    "jdk.management.agent" 
    "jdk.sctp" 
    "java.management"
    "jdk.unsupported"
    "java.instrument" 
    "jdk.management" 
    "jdk.nio.mapmode"
    "java.management.rmi" 

b、扩展加载器(jdk8前(Extension ClassLoader),jdk8后叫平台类加载器(PlatformClassLoader))

jdk8
    它负责加载jre/lib/ext扩展库
    它是ClassLoader的子类
jdk8后 平台类加载器
    "java.sql"
    "jdk.charsets"
    "java.transaction.xa" 
    "java.xml.crypto"
    "jdk.xml.dom"
    "jdk.httpserver"
    "jdk.crypto.cryptoki"
    "java.net.http"
    "jdk.zipfs"
    "jdk.crypto.ec"
    "jdk.crypto.mscapi" 
    "jdk.jsobject"
    "java.sql.rowset" 
    "java.smartcardio"
    "java.security.jgss"
    "jdk.security.auth"
    "java.compiler" 
    "java.scripting" 
    "jdk.dynalink"
    "jdk.accessibility"
    "jdk.security.jgss" 
    "jdk.naming.dns" 
    "jdk.localedata"

c、应用程序类加载器(APplication ClassLoader)

加载如下内容
"jdk.jdi" 
"jdk.jstatd" 
"jdk.random"
"jdk.internal.ed"
"jdk.compiler" 
"jdk.internal.opt" 
"jdk.jconsole"
"jdk.attach"
"jdk.javadoc" 
"jdk.jshell" 
"jdk.editpad" 
"jdk.internal.le"
"jdk.jlink"
"jdk.jdwp.agent" 
"jdk.internal.jvmstat" 
"jdk.unsupported.desktop"
"jdk.jdeps"
"jdk.jartool"
"jdk.jpackage" 

d、自定义类加载器

当你的程序需要加载“特定”目录下的类,可以自定义类加载器;
当你的程序的字节码文件需要加密时,那么往往会提供一个自定义类加载器对其进行解码
后面会见到的自定义类加载器:tomcat中
java系统类加载器的双亲委派模式(parents delegate)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ZdDhC9k2-1692363825586)(F:\Typora\images\JavaSE\image-20230809103726176.png)]

简述

下一级的类加载器,如果接到任务时,会先搜索是否加载过,如果没有,会先把任务往上传,如果都没有加载过,一直到根加载器,如果根加载器在它负责的路径下没有找到,会往回传,如果一路回传到最后一级都没有找到,那么会报ClassNotFoundException或NoClassDefError,如果在某一级找到了,就直接返回Class对象。

优点

1、采用双亲委派模式的好处是Java类随着它的类加载器一起具备了一种带有优先级的层次关系,通过这种层级关系可以避免类的重复加载,当父亲已经加载了该类,就没有必要让子类加载器再加载一次

2、安全,java核心api中定义类型不会被随意替换。假设通过网络传递一个名为java.lang.Integer的类,通过双亲委托模式传递到启动类加载器,而启动类加载器在核心Java API发现这个名字的类,发现该类已被加载,并不会重新加载网络传递的过来的java.lang.Integer,而直接返回已加载过的Integer.class,这样便可以防止核心API库被随意篡改。

注意:

应用程序类加载器 把 扩展类加载器视为父加载器,

扩展类加载器 把 引导类加载器视为父加载器。

12.1.4、类的卸载

1、该类的所有实例偶读已经被回收,也就是java堆中不存在该类的任何实例

2、加载该类的ClassLoader已经被回收

3、该类对应的java.lang.Class对象没有任何地方被引用,无法在任何地方通过反射访问该类的方法

如果以上三个条件全部满足,jvm就会在方法区垃圾回收的时候对类进行卸载,类的卸载过程其实就是在方法区中清空类信息,java类的整个生命周期就结束了

12.2、java.lang.Class类

12.2.1、那些对象可以获取Class对象
//(1)基本数据类型和void
例如:int.class
	 void.class
//(2)类和接口
例如:String.class
	Comparable.class
//(3)枚举
例如:ElementType.class
//(4)注解
例如:Override.class
//(5)数组
例如:int[].class
12.2.2、获取Class对象的四种方式
  • 类型名.class
Class<String> stringClass = String.class;
  • 对象名.getClass()
String s = new String();
s.getClass();
  • Class.forName(全类名)
Class<?> aClass = Class.forName("java.lang.String");
  • ClassLoader的类加载器对象.loadClass(全类名)
//用其他的类加载对象去调用getClassLoader().loadClass()方法
Class<Person> personClass = Person.class;
Class<?> aClass1 = personClass.getClassLoader().loadClass("java.lang.String");
System.out.println("aClass1 = " + aClass1);

12.3、反射的应用

12.3.1、获取类型的详细信息
public class TestClassInfo {
	public static void main(String[] args) throws NoSuchFieldException, SecurityException {
		//1、先得到某个类型的Class对象
		Class clazz = String.class;
		//比喻clazz好比是镜子中的影子
		
		//2、获取类信息
		//(1)获取包对象,即所有java的包,都是Package的对象
		Package pkg = clazz.getPackage();
		System.out.println("包名:" + pkg.getName());
		
		//(2)获取修饰符
		//其实修饰符是Modifier,里面有很多常量值
		/*
		 * 0x是十六进制
		 * PUBLIC           = 0x00000001;  1    1
		 * PRIVATE          = 0x00000002;  2	10
		 * PROTECTED        = 0x00000004;  4	100
		 * STATIC           = 0x00000008;  8	1000
		 * FINAL            = 0x00000010;  16	10000
		 * ...
		 * 
		 * 设计的理念,就是用二进制的某一位是1,来代表一种修饰符,整个二进制中只有一位是1,其余都是0
		 * 
		 * mod = 17          0x00000011
		 * if ((mod & PUBLIC) != 0)  说明修饰符中有public
		 * if ((mod & FINAL) != 0)   说明修饰符中有final
		 */
		int mod = clazz.getModifiers();
		System.out.println(Modifier.toString(mod));
		
		//(3)类型名
		String name = clazz.getName();
		System.out.println(name);
		
		//(4)父类,父类也有父类对应的Class对象
		Class superclass = clazz.getSuperclass();
		System.out.println(superclass);
		
		//(5)父接口们
		Class[] interfaces = clazz.getInterfaces();
		for (Class class1 : interfaces) {
			System.out.println(class1);
		}
		
		//(6)类的属性,  你声明的一个属性,它是Field的对象
/*		Field clazz.getField(name)  根据属性名获取一个属性对象,但是只能得到公共的
		Field[] clazz.getFields();  获取所有公共的属性
		Field clazz.getDeclaredField(name)  根据属性名获取一个属性对象,可以获取已声明的
		Field[] clazz.getDeclaredFields()	获取所有已声明的属性
		*/
		Field valueField = clazz.getDeclaredField("value");
//		System.out.println("valueField = " +valueField);
		
		Field[] declaredFields = clazz.getDeclaredFields();
		for (Field field : declaredFields) {
			//修饰符、数据类型、属性名    
			int modifiers = field.getModifiers();
			System.out.println("属性的修饰符:" + Modifier.toString(modifiers));
			
			String name2 = field.getName();
			System.out.println("属性名:" + name2);
			
			Class<?> type = field.getType();
			System.out.println("属性的数据类型:" + type);
		}
		System.out.println("-------------------------");
		//(7)构造器们
		Constructor[] constructors = clazz.getDeclaredConstructors();
		for (Constructor constructor : constructors) {
			//修饰符、构造器名称、构造器形参列表  、抛出异常列表
			int modifiers = constructor.getModifiers();
			System.out.println("构造器的修饰符:" + Modifier.toString(modifiers));
			
			String name2 = constructor.getName();
			System.out.println("构造器名:" + name2);
			
			//形参列表
			System.out.println("形参列表:");
			Class[] parameterTypes = constructor.getParameterTypes();
			for (Class parameterType : parameterTypes) {
				System.out.println(parameterType);
			}
            
            //异常列表
			System.out.println("异常列表:");
			Class<?>[] exceptionTypes = constructor.getExceptionTypes();
			for (Class<?> exceptionType : exceptionTypes) {
				System.out.println(exceptionType);
			}
		}
		System.out.println("=--------------------------------");
		//(8)方法们
		Method[] declaredMethods = clazz.getDeclaredMethods();
		for (Method method : declaredMethods) {
			//修饰符、返回值类型、方法名、形参列表 、异常列表 
			int modifiers = method.getModifiers();
			System.out.println("方法的修饰符:" + Modifier.toString(modifiers));
			
			Class<?> returnType = method.getReturnType();
			System.out.println("返回值类型:" + returnType);
			
			String name2 = method.getName();
			System.out.println("方法名:" + name2);
			
			//形参列表
			System.out.println("形参列表:");
			Class[] parameterTypes = method.getParameterTypes();
			for (Class parameterType : parameterTypes) {
				System.out.println(parameterType);
			}
			
			//异常列表
			System.out.println("异常列表:");
			Class<?>[] exceptionTypes = method.getExceptionTypes();
			for (Class<?> exceptionType : exceptionTypes) {
				System.out.println(exceptionType);
			}
		}
		
	}
}

12.3.2、创建任意引用类型的对象

两种方式:

1、直接通过Class对象来实例化(要求必须有无参构造)

2、通过获取构造器对象来进行实例化

方式一的步骤:

(1)获取该类型的Class对象(2)创建对象

@Test
	public void test2()throws Exception{
		Class<?> clazz = Class.forName("com.atguigu.test.Student");
		//Caused by: java.lang.NoSuchMethodException: com.atguigu.test.Student.()
		//即说明Student没有无参构造,就没有无参实例初始化方法
		Object stu = clazz.newInstance();
		System.out.println(stu);
	}
	
	@Test
	public void test1() throws ClassNotFoundException, InstantiationException, IllegalAccessException{
//		AtGuigu obj = new AtGuigu();//编译期间无法创建
		
		Class<?> clazz = Class.forName("com.atguigu.test.AtGuigu");
		//clazz代表com.atguigu.test.AtGuigu类型
		//clazz.newInstance()创建的就是AtGuigu的对象
		Object obj = clazz.newInstance();
		System.out.println(obj);
	}

方式二的步骤:

(1)获取该类型的Class对象(2)获取构造器对象(3)创建对象

如果构造器的权限修饰符修饰的范围不可见,也可以调用setAccessible(true)

示例代码:

public class TestNewInstance {
	@Test
	public void test3()throws Exception{
		//(1)获取Class对象
		Class<?> clazz = Class.forName("com.atguigu.test.Student");
		/*
		 * 获取Student类型中的有参构造
		 * 如果构造器有多个,我们通常是根据形参【类型】列表来获取指定的一个构造器的
		 * 例如:public Student(int id, String name) 
		 */
		//(2)获取构造器对象
		Constructor<?> constructor = clazz.getDeclaredConstructor(int.class,String.class);
		
		//(3)创建实例对象
		// T newInstance(Object... initargs)  这个Object...是在创建对象时,给有参构造的实参列表
		Object obj = constructor.newInstance(2,"张三");
		System.out.println(obj);
	}
	
}
12.3.3、操作任意类型的属性

1、获取该类的Class对象

2、获取属性对象

3、设置属性可访问 field.setAccessible(true);

4、创建实例对象:如果操作的是非静态属性,需要创建实例对象

5、设置属性值

6、获取属性值

注意

如果操作静态变量,那么实例对象可以省略,用null表示

public class TestField {
	public static void main(String[] args)throws Exception {
		//1、获取Student的Class对象
		Class clazz = Class.forName("com.atguigu.test.Student");
		
		//2、获取属性对象,例如:id属性
		Field idField = clazz.getDeclaredField("id");
        
        //3、如果id是私有的等在当前类中不可访问access的,我们需要做如下操作
		idField.setAccessible(true);
		
		//4、创建实例对象,即,创建Student对象
		Object stu = clazz.newInstance();
				
		//5、获取属性值
		/*
		 * 以前:int 变量= 学生对象.getId()
		 * 现在:Object id属性对象.get(学生对象)
		 */
		Object value = idField.get(stu);
		System.out.println("id = "+ value);
		
		//6、设置属性值
		/*
		 * 以前:学生对象.setId(值)
		 * 现在:id属性对象.set(学生对象,值)
		 */
		idField.set(stu, 2);
		
		value = idField.get(stu);
		System.out.println("id = "+ value);
	}
}
12.3.4、调用任意类型的方法

1、获取该类型的Class对象

2、获取方法对象

3、创建实例对象

4、调用方法

注意

如果方法的权限修饰符修饰的范围不可见,也可以调用setAccessible(true)

如果方法是静态方法,实例对象也可以省略,用null代替

public class Person {
    String name;
    public Person() {
    }
    public Person(String name) {
        this.name = name;
    }
    public void show(){
        System.out.println("Person show()");
    }
     private   boolean isEven(int num){
        return num%2==0;
    }
    public static int getSum(int a,int b){
        return a+b;
    }
    private static String concat(String a,String b,int c){
        return a+b+c;
    }
}
public class PersonTest {

    @Test
    public void test05() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        //todo 私有的静态方法
        //1.创建Class对象
        Class<Person> personClass = Person.class;
        //2.获取指定的方法
        Method concat = personClass.getDeclaredMethod("concat", String.class, String.class, int.class);
        //3.设置私有方法可以操作
        concat.setAccessible(true);
        //4.执行方法 获取结果
        Object result = concat.invoke(null, "貂蝉", "妲己", 666);
        //5.展示结果
        System.out.println("result = " + result);


    }

    @Test
    public void test04() throws Exception{
        //todo isEven
        //1.创建Class对象
        Class<?> aClass = Class.forName("com.atguigu.reflect01.method05.Person");
        //2.获取指定的方法
        Method isEven = aClass.getDeclaredMethod("isEven", int.class);
        isEven.setAccessible(true);
        //3.创建对象
        Object o = aClass.newInstance();
        //4.执行方法
        Object result = isEven.invoke(o, 19);
        System.out.println("result = " + result);
    }
    
    @Test
    public void test03() throws ClassNotFoundException, NoSuchMethodException, InstantiationException, IllegalAccessException, InvocationTargetException {
        //todo 反射调用 show()
        //1.创建Class对象
        Class<?> aClass = Class.forName("com.atguigu.reflect01.method05.Person");
        //2.获取指定的方法
        Method show = aClass.getMethod("show");
        //3.创建对象
        Object o = aClass.newInstance();
        //4.执行方法
        show.invoke(o);
    }
    
    @Test
    public void test02(){
        Person p = new Person("张三");
        p.show();
        int sum = Person.getSum(10, 39);
        System.out.println("sum = " + sum);
    }

    @Test
    public void test01(){
        //1.创建Class对象
        Class<Person> personClass = Person.class;
        //2.获取所有的方法
       //2.1获取父子类内所有公共的方法
        //Method[] methods = personClass.getMethods();
        //2.2本类中所有的方法
        Method[] methods = personClass.getDeclaredMethods();
        for (Method method : methods) {
            System.out.println("method = " + method);
        }
    }
}

12.4、反射突破泛型限制

  • 原理:泛型处于编译时,反射位于运行时。
public class ListTest {
    @Test
    public void test01() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        //1.创建集合对象
        ArrayList<String> list = new ArrayList();
        list.add("张三");
        //1.创建Class对象
        Class aClass = list.getClass();
        //2.获取方法
        Method addMethod = aClass.getDeclaredMethod("add", Object.class);
        //3.执行方法
        addMethod.invoke(list,1021);
        System.out.println(list);
    }
}

13、注解

  • 注解(Annotation),也称元数据,一种代码级别的说明,是JDK1.5及之后版本引入的一个特性,与类、接口、枚举是同一层次的。它可以声明在包、类、字段、局部变量、方法参数等的前面,用来对这些元素进行说明,注释。

13.1、自定义注解

  • 自定义注解本质继承Annotation
  • 注解内的属性的数据类型
    • 基本数据类型
    • 枚举
    • String
    • Class类型
    • 以前它们的数组类型
@interface 注解名{
    数据类型 属性名() [default];
    数据类型 属性名() [default];
}

13.2、元注解

  • @Target:用来限制注解的使用范围

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-V7CLHqVy-1692363825586)(F:\Typora\images\JavaSE\image-20230809180135902.png)]

  • @Retention:指定其所修饰的注解的保留策略

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8FOGqxGQ-1692363825586)(F:\Typora\images\JavaSE\image-20230809180119272.png)]

  • @Document:该注解是一个标记注解,用于指示一个注解将被文档化
  • @Inherited:该注解使父类的注解能被其子类继承
  • @Repeatable:该注解是Java8新增的注解,用于开发重复注解

14、Lambda

  • 作用:简化函数式接口

14.1、函数式接口

  • 函数式接口:只有一个抽象方法需要实现的接口(@FunctionalInterface)

内置函数式接口

1、消费型函数式接口 有形参没有返回值

2、供给型函数式接口 没有形参没有返回值

3、判断型函数式接口 有形参有返回值,但返回值为布尔型

4、功能型函数式接口 有形参有返回值

14.2、Lambda的使用

  • 简化

1、形参类型可以省略

2、如果只有一个形参 () 可以省略

3、如果方法体只有一条语句,可以省略{}

4、带返回值方法的,只有一条返回语句可以省略 return

public interface Student {
    void show();
}

interface A{
    int sum(int a,int b);
}
public class StudentTest {
    @Test
    public void test01(){
        //Student s = StudentTest::show;
        Student s = ()-> System.out.println("show.....");
        s.show();
    }
    @Test
    public void test02(){
        //A a = (int a1,int b)-> a1 + b;
        /*A a = (int a1,int b)-> {
            return a1 + b
        };*/
        //A a = (a1, b) -> a1+b;
        A a = Integer::sum;
        System.out.println("a.sum(10,21) = " + a.sum(10, 21));
    }
}

算法

1、递归

/*
描述:猴子吃桃子问题,
猴子第一天摘下若干个桃子,
当即吃了所有桃子的一半,还不过瘾,又多吃了一个。
第二天又将仅剩下的桃子吃掉了一半,又多吃了一个。
以后每天都吃了前一天剩下的一半多一个。到第十天,只剩下一个桃子。
试求第一天共摘了多少桃子?
1 
4 
10
22
 */
public class MonkeyTest {
    public static void main(String[] args) {
        System.out.println(monkey(1));
    }
    public static int monkey(int day){
        if (day==10){
            return 1;
        }
        return (monkey(day+1)+1)*2;
    }
}
//斐波那契数列  1 1 2 3 5 8
public class FeiBo {
    public static void main(String[] args) {
        System.out.println("getFeiBo(5) = " + getFeiBo(6));
    }
    public static int getFeiBo(int num){
        if (num == 1 || num == 2){
            return 1;
        }
        return getFeiBo(num-1)+getFeiBo(num-2);
    }
}

2、二分查找

public class BinarySearch {
    public static void main(String[] args) {
        int arr[] = {1,2,3,4,5,6,7,8};
        System.out.println("binarySearch(arr,1) = " + binarySearch(arr, 1));
    }
    public static int binarySearch(int arr[],int num){
        //定义开始
        int start = 0;
        //定义结束
        int end = arr.length-1;

        while (start <= end){
            int mid = (start+end)/2;
            int midValue = arr[mid];
            if (midValue < num){
                start = mid+1;
            } else if(midValue > num){
                end = mid -1;
            } else {
                return mid;
            }
        }
        return -1;
    }
}

3、选择排序

public class SelectSort {
    public static void main(String[] args) {
        int arr[] = {5,7,12,10,21,26,9};
        System.out.println("排序前");
        System.out.println(Arrays.toString(arr));
		//需要进行几次排序
        for (int i = 0; i < arr.length-1; i++) {
            int minIndex = i;
            //需要和后面的几个元素依次对比
            for (int j = i+1; j <arr.length ; j++) {
                if (arr[j]<arr[minIndex]){
                    minIndex = j;
                }
            }
            //如果最小元素下标不是i,那么就进行元素
            if (i!=minIndex){
                int temp = arr[i];
                arr[i] = arr[minIndex];
                arr[minIndex] = temp;
            }
        }
        System.out.println("排序后");
        System.out.println(Arrays.toString(arr));
    }
}

4、冒泡排序

//冒泡排序及优化
//arr.length-1,一个长度为arr.length的数组只需要执行 arr.length-1次即可有序
for(int i = 0; i < arr.length-1;i++){ 
    boolean flag = true;
    for(int j = 0;j < arr.length-i-1;j++){ //arr.length-i-1-->让排序只注重于无序部分
        if(arr[j] > arr[j+1]){
            int temp = arr[j];
            arr[j] = arr[j+1];
            arr[j+1] = temp;
            flag = false;
        }
    }
    if(flag){
        berak;//如果提前排序完,就提前退出
    }
}

Arrays.toString(arr);

你可能感兴趣的:(Java从零开始,开发语言,java)