java笔记1/3 (B站hsp学java)

JAVA基础

文章目录

变量

image-20211026103857848

整形(INT)

  1. Java各整数类型有固定的范围和字段长度,不受具体OS[操作系统]的影响,以
    保证java程序的可移植性。
  2. Java的整型常量(具体值)默认为int型,声明long型常量须后加1或L
  3. java程序中变量常声明为int型,除非不足以表示大数,才使用long
  4. bit:计算机中的最小存储单位。byte:计算机中基本存储单元,1byte =8 bit
类型占用存储空间
byet[字节]1字节
short[短整型]2字节
int[整形]4字节
long[长整型]8字节

浮点型(float/double)

1.与整数类型类似,Java浮点类型也有固定的范围和字段长度,不受具体OS的影响。

2**.Java的浮点型常量默认为double型**,声明float型常量,须后加‘f或‘F

3.浮点型常量有两种表示形式十进制数形式:如:5.12 512.0f .512(必须有小数点)

4.科学计数法形式:如:5.12e2[5.12*10的2次方] 5.12E-2 [5.12/10的2次方]

5.通常情况下,应该使用double型,因为它比float型更精确。

类型占用存储空间
单精度float4字节
双精度double8字节
double num11 = 2.7;
double num12 = 8.1 / 3;
system.out.println(num12);//接近2.7的一个小数,而不是2.7
//这是因为,我们在double num12 = 8.1中存储时,计算机不知道我们的后面存储的是啥,有可能是8.10000001,自然运算结果就是一个接近2.7的小数
//得到一个重要的使用点:当我们对运算结果是小数的进行相等判断是,要小心
//应该是以两个数的差值的绝对值,在某个精度范围类判断
if(num11 == num12) {
    system.out.println("相等");
}
//正确的写法,
if(Math.abs (num11 - num12)< 6.000001 ) {
    system.out.println("差值非常小,到我的规定精度,认为相等...");
}
//可以通过java API来看
System.out.println(Math.abs ( num11 - num12));
//细节:如果是直接查询得的的小数或者直接赋值,是可以判断相等

字符型(char)

1.字符常量是用单引号(‘’)括起来的单个字符。例如:char c1 = ‘a’; char c2 = ‘中’; char c3 = ‘9’;

2.Java中还允许使用转义字符’‘来将其后的字符转变为特殊字符型常量。例如:char c3 = ‘\n’; //’\n’表示换行符

3.在java中,char的本质是一个整数,在输出时,是unicode码对应的字符

4.可以直接给char赋一个整数,然后输出时,会按照对应的unicode字符输出

5.char类型是可以进行运算的,相当于一个整数,因为它都对应有Unicode码.

字符类型本质探讨
1.字符型存储到计算机中,需要将字符对应的码值(整数)找出来,比如’a’
存储:‘a’ >码值97>二进制(110 0001) >存储
读取:二进制=>97
=> ‘a’=>显示
2.字符和码值的对应关系是通过字符编码表决定的(是规定好)
ASCIl (ASCII编码表一个字节表示,一共128个字符)

​ Unicode (Unicode编码表固定大小的编码使用两个字节来表示字符,字母和汉字统一都是字节,这样浪费空间)

​ utf-8(编码表,大小可变的编码字母使用1个字节,汉字使用3个字节)

​ gbk(可以表示汉字,而且范围广,字母使用1个字节,汉字2个字节)

​ gb2312(可以表示汉字,gb2312<gbk)

​ big5码(繁体中文,台湾,香港)

布尔类型(boolean)

1.布尔类型也叫boolean类型,booolean类型数据只允许取值true和false,无null

2.boolean类型占1个字节。

3.boolean类型适于逻辑运算,一般用于程序流程控制

不可以0或非0的整数替代false和true,这点和C语言不同

基础数据类型的转换

自动类型转换

1.有多种类型的数据混合运算时,系统首先自动将所有数据转换成容量最大的那种数据类型,然后再进行计算。

2.当我们把精度(容量)大的数据类型赋值给精度(容量)小的数据类型时,就会报错,反之就会进行自动类型转换。(自动类型换只能小转大)

3.(byte, short)和char之间不会相互自动转换。

4.byte,short, char他们三者可以计算,在计算时首先转换为int类型。

5.boolean不参与转换

6.自动提升原则:表达式结果的类型自动提升为操作数中最大的类型

byte b2 = 1;
byte b3 = 2;
short s1 = 1;
// short s2 = b2 + s1;//错,b2 + s1 => int
int s2 = b2 + s1;//对,b2 + s1 => int
byte b4 = b2 + b3;//错误:b2 + b3 => int
强制类型转换

自动类型转换的逆过程,将容量大的数据类型转换为容量小的数据羑型。使用时要加上强制转换符(),但可能造成精度降低或溢出,格外要注意。

1.当进行数据的大小从大->小,就需要使用到强制转换

2.强转符号只针对于最近的操作数有效,往往会使用小括号提升优先级

3.char类型可以保存int的常量值,但不能保存int的变量值,需要强转

4.byte和short类型在进行运算时,当做int类型处理。

//强转符号只针对于最近的操作数有效,往往会使用小括号提升优先级
//int x = (int)10*3.5+6*1.5;//编译错误:double -> int
int x = (int) (10*3.5+6*1.5);// (int)44.0 ->44
system.out.println(x);//44
char c1 = 100;/ /ok
int m = 100; //ok
// char c2 = m;//错误
char c3 = (char)m;//ok
system.out.println(c3);//100对应的字符,d字符

重载

注意事项和使用细节:

1.方法名:必须相

2.形参列表:必须不同(形参类型或个数或顺序,至少有一样不同,参数名无要求)3)返回类型:无要求

可变参数

基本概念

java允许将同一个类中多个同名同功能但参数个数不同的方法,封装成一个方法。

基本语法

访问修饰符返回类型方法名(数据类型…形参名){}

例如:方法 sum【可以计算2个数的和,3个数的和,4,5, 。。】

//1. int...表示接受的是可变参数,类型是int ,即可以接收多个int(0-多)
//2.使用可变参数时,可以当做数组来使用即numS可以当做数组
// 3.逼历nums求和即可
public int sum(int. . . nums) { 
    // System.out.println("接收的参数个数="+ nums.length);int res = 0;
    for(int i = 0; i < nums. length; i++){
        res += nums[i];
    }
    return res;
}

注意事项和使用细节

1.可变参数的实参可以为0个或任意多个。

2.可变参数的实参可以为数组。

3.可变参数的本质就是数组.

4.可变参数可以和普通类型的参数一起放在形参列表,但必须保证可变参数在最后

5.一个形参列表中只能出现一个可变参数

属性

1.属性的定义语法同变量,示例:访问修饰符属性类型属性名;

有四种访问修饰符public, proctected,默认, private

2.属性的定义类型可以为任意类型,包含基本类型或引用类型

3.属性如果不赋值,有默认值,规则和数组一致。

具体说: int 0, short 0.byte 0, long O, float 0.0,double 0.0,char \u0000, boolean false,String null

作用域

1.在java编程中,主要的变量就是属性(成员变量)和局部变量。

2.我们说的局部变量一般是指在成员方法中定义的变量。【举例Cat类:cry)

3.java中作用域的分类
全局变量:也就是属性,作用域为整个类体
局部变量:也就是除了属性之外的其他变量,作用域为定义它的代码块中

4**.全局变量可以不赋值,直接使用,因为有默认值,局部变量必须赋值后,才能使**
用,因为没有默认值。

注意事项和细节使用

1.属性和局部变量可以重名,访问时遵循就近原则。

2.在同一个作用域中,比如在同一个成员方法中,两个局部变量,不能重名。

3.属性生命周期较长,伴随着对象的创建而创建,伴随着对象的死亡而死亡。局部变量,生命周期较短,伴随着它的代码块的执行而创建,伴随着代码块的结束而死亡。即在一次方法调用过程中。

4、作用域不同

全局变量:可以被本类使用,或其他类使用(通过对象调用)

局部变量:只能在本类中对应的方法中使用

5.修饰符不同

全局变量/属性可以加修饰符

局部变量不可以加修饰符

javap的使用

  1. javap是JDK提供的一个命令行工具,javap能对给定的class文件提供的字节代码进行反编译

  2. 通过它,可以对照源代码和字节码,从而了解很多编译器内部的工作,对更深入地理解如何提高程序执行的效率等问题有极大的帮助。

  3. 使用格式
    javap 常用: javap -c -v 类名

    其中, 可能的选项包括:
    -help --help -? 输出此用法消息
    -version 版本信息
    -v -verbose 输出附加信息
    -l 输出行号和本地变量表
    -public 仅显示公共类和成员
    -protected 显示受保护的/公共类和成员
    -package 显示程序包/受保护的/公共类和成员 (默认)
    -p -private 显示所有类和成员
    -c 对代码进行反汇编
    -s 输出内部类型签名
    -sysinfo 显示正在处理的类的系统信息 (路径, 大小, 日期, MD5 散列)
    -constants 显示最终常量
    -classpath 指定查找用户类文件的位置
    -cp 指定查找用户类文件的位置

构造方法(构造器)

基本概念

构造方法是初始化对象,不是创造对象

[修饰符] 方名(形参列表){
方法体;
}

1)构造器的修饰符可以默认,也可以是public protected private

2)构造器没有返回值

3)方法名和类名字必须一样

4)参数列表和成员方法一样的规则

5)构造器的调用系统完成(在创建对象时,系统会自动的调用该类的构造器完成对对象的初始化。

注意事项和使用细节

1.一个类可以定义多个不同的构造器,即构造器重载
比如:我们可以再给Person类定义一个构造器,用来创建对象的时候,只指定人名,不需要指定年龄

2.构造器名和类名要相同

3.构造器没有返回值

4.构造器是完成对象的初始化,并不是创建对象

5.在创建对象时,系统自动的调用该类的构造方法

6.如果程序员没有定义构造器,系统会自动给类生成一个默认无参构造方法(也叫默认构造方法),

比如Dog(){},使用javap指令反编译看看

image-20211027201703484

7.一旦定义了自己的构造器,默认的构造器就覆盖了,就不能再使用默认的无参构造器,除非显式的定义一下,即: Dog(){}

对象创建流程分析

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-PRQAWGhk-1636722245107)(http://www.gdx0326.top:20014/static/java笔记.assets/image-20211027203420273.png)]

1.加载Person信息(Person.class),只会加载一次

2.在堆中分配空间(地址)

3.完成对象的初始化

​ 3.1 默认初始化 age = 0 name = null

​ 3.2显示初始化 age = 90 name = null

​ 3.4构造器初始化 age = 20 name = 小倩

4.将对象在堆中的地址。返回给p(p是对象名,也可以理解是对象的引用)

this

什么是this

java虚拟机会给每个对象分配 this,代表当前对象。

使用this解决了变量命名问题

this本质的理解:

image-20211027210335775

this的注意害项和使用细节

1.this关键字可以用来访问本类的属性、方法、构造器

2.this用于区分当前类的属性和局部变量

3.访问成员方法的语法:this.方法名(参数列表);

4.访问构造器语法:this(参数列表);注意只能在构造器中使用(即只能在构造器中访问另外一个构造器)

注意:访问构造器语法:this(参数列表);必须放置第一条语句

5.this不能在类定义的外部使用,只能在类定义的方法中使用。

包的三大作用

1.区分相同名字的类

2.当类很多时,可以很好的管理类[例如Java API文档]

3.控制访问范围

包的本质分析(原理)

包的本质:实际上就是创建不同的文件夹来保存类文件

image-20211027213325889

包的命名

命名规则:
只能包含数字、字母、下划线、小圆点“.”,但不能用数字开头,不能是关键字或保留字

demo.class.exec1//错误,使用了关键字
demo.12a//错误,数字开头
demo.ab12.oa/正确

命名规范

一般是小写字母+小圆点一般是com.公司名.项目名.业务模块名

比如:com.hspedu.oa.model; com.hspedu.oa.controller;

举例:
com.sina.crm.user/用户模块

com.sina.crm.order//订单模块

com.sina.crm.utils //工具类

常用的包

一个包下,包含很多的类,java中常用的包有:
java.lang.* //lang包是基本包,默认引入,不需要再引入.
java.util.* //util包,系统提供的工具包,工具类,使用 Scanner

java.net.* //网络包,网络开发
java.awt.* //是做java的界面开发,GUI

注意事项和使用细节

1.package的作用是声明当前类所在的包,需要放在class的最上面,一个类中最多只有一句package

2.import指令位置放在package的下面,在类定义前面,可以有多句且没有顺序要求。

访问修饰符

基本介绍

java提供四种访问控制修饰符号控制方法和属性(成员变量)的访问权限(范围):

1.公开级别:用public修饰,对外公开

2.受保护级别:用protected修饰,对子类和同一个包中的类公开

3.默认级别:没有修饰符号,向同一个包的类公开

4.私有级别:用private修饰,只有类本身可以访问,不对外公开

权限大小顺序:public > protected >默认>private

访问级别访问控制修饰符同类同包子类不同包
公开public
受保护protected×
默认没有修饰符号××
私有private×××

使用的注意事项

1)修饰符可以用来修饰类中的属性,成员方法以及类

2)只有默认的和public才能修饰类!,并且遵循上述访问权限的特点。

3)因为没有学习继承,因此关于在子类中的访问权限

4)成员方法的访问规则和属性完全一样.

封装

封装介绍

封装(encapsulation)就是把抽象出的数据[属性]和对数据的操作[方法]封装在一起,数据被保护在内部,程序的其它部分只有通过被授权的操作[方法],才能对数据进行操作。

封装的理解和好处

1.隐藏实现细节:方法(连接数据库)<–调用(传入参数…)

2.可以对数据进行验证,,保证安全合理

封装的实现步骤

1)将属性进行私有化【不能直接修改属性】

2)提供一个公共的(public)set方法,用于对属性判断并赋值

public void setXxx(类型 参数名){
	//加入数据验证的业务
	逻辑属性=参数名;
}

3)提供一个公共的(public)get方法,用于获取属性的值

public 数据类型 getXxx(){//权限判断
	return xx;
}

继承

继承基本介绍和示意图

继承可以解决代码复用,让我们的编程更加靠近人类思维.当多个类存在相同的属性(变量)和方法时,可以从这些类中抽象出父类,在父类中定义这些相同的属性和方法,所有的子类不需要重新定义这些属性和方法,只需要通过extends来声明继承父类即可。
image-20211028163811105

继承的基本语法

class 子类 extends 父类{
    
}

1.子类就会自动拥有父类定义的属性和方法

2.父类又叫超类,基类。

3.子类又叫派生类。

继承给编程带来的便利

1.代码的复用性提高了

2.代码的扩展性和维护性提高了

继承的深入讨论/细节问题

1.子类继承了所有的属性和方法,非私有的属性和方法可以直接访问,但是私有属性和方法,不能在子类直接访问,要通过父类提供的公共的方法去访问

2.子类必须调用父类的构造器,完成父类的初始化

3.当创建子类对象时,不管使用子类的哪个构造器,默认情况下总会去调用父类的无参构造器,如果父类没有提供无参构造器,则必须在子类的构造器中用super去指定使用父类的哪个构造器完成对父类的初始化工作,否则,编译不会通过[举例说明]

4.如果希望指定去调用父类的某个构造器,则显式的调用一下

5.super在使用时,必须放在构造器第一行

6.super()和 this()都只能放在构造器第一行,因此这两个方法不能共存在一个构造器

7.java所有类都是Object类的子类, Object是所有类的基类

8.父类构造器的调用不限于直接父类!将一直往上追溯直到Object类(顶级父类)

9.子类最多只能继承一个父类(指直接继承),即java中是单继承机制。

10.不能滥用继承,子类和父类之间必须满足is-a的逻辑关系

继承的本质分析

找属性时,顺序是:

(1)首先看子类是否有该属性

(2)如果子类有这个属性,并且可以访问,则返回信息

(3)如果子类没有这个属性,就看父类有没有这个属性(如果父类有该属性,并且可以访问,就返回信息…)

(4)如果父类没有就按照(3)的规则,继续找上级父类,直到Object. . .

class GrandPa {//爷类
	string name ="大头爷爷";
    string hobby ="旅游";
}
class Father extends GiandPa {//父类
	string name = "大头爸爸";
	int age = 39;
}
class Son extends Father {//子类
	string name ="大头儿子";
}

image-20211028172728657

super关键字

基本介绍

super代表父类的引用,用于访问父类的属性、方法、构造器●基本语法

1.访问父类的属性,但不能访问父类的private属

super.属性名;

2.访问父类的方法,不能访问父类的private方法

super.方法名(参数列表);

3.访问父类的构造器(这点前面用过):

super(参数列表);//只能放在构造器的第一句,只能出现一句!

super给编程带来的便利/细节

1.调用父类的构造器的好处(分工明确,父类属性由父类初始化,子类的属性由子类初始化)

2.当子类中有和父类中的成员(属性和方法)重名时,为了访问父类的成员,必须通过super。如果没有重名,使用super、this、直接访问是一样的效果!

3.super的访问不限于直接父类,如果爷爷类和本类中有同名的成员,也可以使用super去访问爷爷类的成员;如果多个基类(上级类)中都有同名的成员,使用super访问遵循就近原则。A->B->C,当然也需要遵守访问权限的相关规则

找类的方法时,顺序是:
(1)先找本类,如果有,则调用

(2)如果没有,则找父类(如果有,并可以调用,则调用

(3)如果父类没有,则继续找父类的父类,整个规则,就是一样的,直到0bject类

提示:

​ 如果查找方法的过程中,找到了,但是不能访问,则报错
​ 如果查找方法的过程中,没有找到,则提示方法不存在

super和this的区别

No.区别点thissuper
1访问属性访问本类中的属性,如果本类没有此属性则从父类开始查找属性从父类中继续查找
2调用方法访问本类中的方法,如果本类没有此方法则从父类继续查找从父类中继续查找
3调用构造器调用本类构造器,必须放在构造器的首行调用父类构造器,必须放在子类构造器的首行
4特殊表示当前对象子类中访问父类对象

方法重写/覆盖

基本介绍

简单的说:方法覆盖(重写)就是子类有一个方法,和父类的某个方法的名称、返回类型、参数一样,那么我们就说子类的这个方法覆盖了父类的那个方法

注意事项和使用细节

方法重写也叫方法覆盖,需要满足下面的条件

1.子类的方法的参数,方法名称,要和父类方法的参数,方法名称完全一样。

2.子类方法的返回类型和父类方法返回类型一样,或者是父类返回类型的子类
比如父类返回类型是Object ,子类方法返回类型是String

3.子类方法不能缩小父类方法的访问权限

多态

基本介绍

多[多种]态[状态]

方法或对象具有多种形态。是面向对象的第三大特征,多态是建立在封装和继承基础之上的。

多态的具体实现

1.方法的多态

2.对象的多态

​ (1)一个对象的编译类型和运行类型可以不一致

​ (2)编译类型在定义对象时,就确定了,不能改变

​ (3)运行类型是可以变化的.

​ (4)编译类型看定义时 = 号的左边,运行类型看 = 号的右边

多态注意事项和细节讨论

多态的前提是:两个对象(类)存在继承关系

多态的向上转型

1.本质:父类的引用指向了子类的对象

2.语法:父类类型 引用名 = new 子类类型();

3)特点:编译类型看左边,运行类型看右边。

可以调用父类中的所有成员(需遵守访问权限),不能调用子类中特有成员;最终运行效果看子类的具体实现!

多态的向下转型

1)语法:子类类型 引用名 = (子类类型) 父类引用;

2)只能强转父类的引用,不能强转父类的对象

3)要求父类的引用必须指向的是当前目标类型的对象

class Animal{
    
}
class Cat extends Animal{
    
}
Animal animal = new Cat();
Cat c = (Cat) animal//必须要有Animal animal = new Cat();才能这样写,否则运行时报错:类异常(类构造)错误

4)当向下转型后,可以调用子类类型中所有的成员

属性没有重写之说

属性没有重写之说!属性的值看编译类型

public static void main(String[] args) {
    System.out.println(base.count);// ? 看编译类型10 
    Sub sub = new Sub();
    System.out.println(sub.count);//? 20
}
class Base { //父类
    int count = 10;//属性
}
class Sub extends Base {//子类
    int count = 20;//属性
}
instanceOf

instanceOf 比较操作符,用于判断对象的运行类型是否为XX类型或XX类型的子类型

java动态绑定机制*

1.当调用对象方法的时候,该方法会和该对象的内存地址/运行类型绑定

2.当调用对象属性时,没有动态绑定机制,哪里声明,那里使用

A a = new B();//向上转型
System.out.println(a.sum());//40-->30(注释1后)
System.out.println(a.sum1());//30-->20(注释2后)

class A{//父类
    public int i= 10;
    
    public int sum() {
        return getI()+ 10;//运行时会动态绑定,从而运行a.getI()
    }
    
    public int sum1(){
        return i+ 10;
    }
    
    public int getl() {
        return i;
    }
}
class B extends A{//子类
    public int i = 20;

//注释1
//	public int sum() {
//        return i+ 20;
//    }
    
	public int getI(){
        return i;//属性时,没有动态绑定机制,哪里声明,那里使用
    }
    
//注释2    
//  public int sum1(){
//  	return i + 10;
//  }
}

多态应用

多态数组

数组的定义类型为父类类型,里面保存的实际元素类型为子类类型

public static void main(String[] args) {
        
    Person[] persons = new Person[5];
    persons[0] = new Person( "jack"20);
    persons[1] = new Student("mary"18100);
    persons[2] = new Student( "smith",1930.1);
    persons[3] = new Teacher( "scott",3020000);
    persons[4] = new Teacher( "king",5025000);
    
    
    //循环遍历多态数组,调用say
	for (int i =0; i < persons.length; i++) {
        //person[i]编译类型是 Person ,运行类型是是根据实际情况有JVM来判断
        System.out.println(persons[i].say();//动态绑定机制
		if(persons[i] instanceof Student){//判断person[i]的运行类型是不是Student
            Student student = (Student)persons[i];//向下转型
			student.study();
			//也可以使用一条语句((Student) persons[i]).study();
		}else if(persons[i] instanceof Teacher) {
			Teacher teacher = (Teacher)persons[i];
			teacher.teach();
        }
    }
}

多态参数

方法定义的形参类型为父类类型,实参类型允许为子类类型

public static void main(String[] args){
    public void showEmpAnnual(Employee e) {
        System.out.println(e.getAnnual());//动态绑定机制。
    }
    //添加一个方法,testWork ,如果是普通员工,则调用work方法,如果是经理,则调用manage方法
    public void testWork(Employee e) {
        if(e instanceof Worker) {
            ((Worker) e).work();//有向下转型操作
        }else if(e instanceof Manager){
            ((Hanager) e).manage();//有向下转型操作
        }
    }
}

Objcet

==运算符

1.==:既可以判断基本类型,又可以判断引用类型

2.==:如果判断基本类型,判断的是值是否相等。示例:int i=10; double d=10.0;

3.==:如果判断引用类型,判断的是地址是否相等,即判定是不是同一个对象

equals方法

4.equals:是Object类中的方法,只能判断引用类型,如何看Jdk源码。

public boolean equals(Object anObject) {
        if (this == anObject) {
            return true;
        }
        if (anObject instanceof String) {
            String aString = (String)anObject;
            if (!COMPACT_STRINGS || this.coder == aString.coder) {
                return StringLatin1.equals(value, aString.value);
            }
        }
        return false;
    }

@HotSpotIntrinsicCandidate
    public static boolean equals(byte[] value, byte[] other) {
        if (value.length == other.length) {
            for (int i = 0; i < value.length; i++) {
                if (value[i] != other[i]) {
                    return false;
                }
            }
            return true;
        }
        return false;
    }

5.默认判断的是地址是否相等,子类中往往重写该方法,用于判断内容是否相等。比如
Integer,String

hashcode

1)提高具有哈希结构的容器的效率!

2)两个引用,如果指向的是同一个对象,则哈希值肯定是一样的!

3)两个引用,如果指向的是不同对象,则哈希值是不一样的

4)哈希值主要根据地址号来的!,不能完全将哈希值等价于地址。

5)案例演示:

public static void main(String[] args) {
    A aa = new A(); 
    A aa2 = new A(); 
    A aa3 = aa;
    System.out.println("aa.hashCode()=" + aa.hashCode());
    System.out.println("aa2.hashCode()=" + aa2.hashcode());
    System.out.println("aa3.hashCode()=" + aa3.hashCode());//与aa.hashCode()相等
}
class A{}

6)后面在集合,中hashCode 如果需要的话,也会重写

toString方法

默认返回:全类名+@+哈希值的十六进制

//Object的toString方法
public String toString() {
    //getClass().getName() 类的全类名(包名+类名)
    //Integer.toHexString(hashCode())将对象的hashCode值转成16进制字符串
    return getClass().getName() + "@" + Integer.toHexString(hashCode());
}

子类往往重写toString方法,用于返回对象的属性信息

重写toString方法,打印对象或拼接对象时,都会自动调用该对象的toString形式.
案例演示:

当直接输出一个对象时,toString方法会被默认的调用

finalize方法

1.当对象被回收时,系统自动调用该对象的finalize方法。子类可以重写该方法,做一些释放资源的操作

//程序员就可以在finalize中,写自己的业务逻辑代码(比如释放资源:数据库连接,或者打开文件..)
//如果程序员不重写 finalize,那么就会调用0bject类的 finalize,即默认处理
//如果程序员重写了finalize,就可以实现自己的逻辑

2.什么时候被回收:当某个对象没有任何引用时,则jvm就认为这个对象是一个垃圾对象,就会使用垃圾回收机制来销毁该对象,在销毁该对象前,会先调用finalize方法。

3.垃圾回收机制的调用,是由系统来决定(即有自己的GC算法),也可以通过System.gc()主动触发垃圾回收机制

我们在实际开发中,几乎不会运用finalize,所以更多就是为了应付面试.

断点调试(debug)

一个实际需求

1.在开发中,新手程序员在查找错误时,这时老程序员就会温馨提示,可以用断点调试,一步一步的看源码执行的过程,从而发现错误所在。

2.重要提示:在断点调试过程中,是运行状态,是以对象的运行类型来执行的.

断点调试介绍

1.断点调试是指在程序的某一行设置一个断点,调试时,程序运行到这一行就会停住,然后你可以一步一步往下调试,调试过程中可以看各个变量当前的值,出错的话,调试到出错的代码行即显示错误,停下。进行分析从而找到这个Bug

2.断点调试是程序员必须掌握的技能。

3.断点调试也能帮助我们查看java底层源代码的执行过程,提高程序员的Java水平。

断点调试的快捷键

F7(跳入):跳入方法内

F8(跳过):逐行执行代码

shift+F8(跳出):跳出方法

F9(resume,执行到下一个断点)

java中段

类变量(静态变量)

JDK7以上版本,静态域存储于定义类型的Class对象中,Class对象如同堆中其他对象一样,存在于GC堆中。

不管static变量在哪里,共识

(1) static变量是同一个类所有对象共享

(2) static类变量,在类加载的时候就生成了.

image-20211029213839724

什么是类变量

类变量也叫静态变量/静态属性,是该类的所有对象共享的变量,任何一个该类的对象去访问它时,取到的都是相同的值,同样任何一个该类的对象去修改它时,修改的也是同一个变量。这个从前面的图也可看出来。

如何定义类变量

访问修饰符 static 数据类型 变量名;//推荐
static 访问修饰符 数据类型 变量名;

如何访问类变量

类名.类变量名//推荐使用
或者 对象名.类变量名//静态变量的访问修饰符的访问权限和范围和普通属性是一样的。

类变量使用注意事项和细节讨论

1.什么时候需要用类变量

当我们需要让某个类的所有对象都共享一个变量时,就可以考虑使用类变量(静态变量):比如:定义学生类,统计所有学生共交多少钱。Student (name, static fee)

2.类变量与实例变量(普通属性)

区别:类变量是该类的所有对象共享的,而实例变量是每个对象独享的。

3.加上static称为类变量或静态变量,否则称为实例变量/普通变量/非静态变量

4.类变量可以通过 类名.类变量名 或者 对象名.类变量名 来访问,但java设计者推荐我们使用 类名.类变量名 方式访问。【前提是满足访问修饰符的访问权限和范围】

5.实例变量(普通变量/属性)不能通过 类名.类变量名 方式访问。

6.类变量是在类加载时就初始化了,也就是说,即使你没有创建对象,只要类加载了,就可以使用类变量了。

7.类变量的生命周期是随类的加载开始,随着类消亡而销毁。

类方法(静态方法)

类方法基本介绍

类方法也叫静态方法。形式如下:

访问修饰符 static 数据返回类型 方法名(){}//推荐
static 访问修饰符 数据返回类型 方法名(){}

类方法的调用:

//前提是满足访问修饰符的访问权限和范围
类名.类方法名
对象名.类方法名

类方法经典的使用场景

当方法中不涉及到任何和对象相关的成员,则可以将方法设计成静态方法,提高开发效率。

如果我们希望不创建实例,也可以调用某个方法(即当做工具来使用),这时,把方法做成静态方法时非常合适

在程序员实际开发,往往会将一些通用的方法,设计成静态方法,这样我们不需要创建对象就可以使用了,比如打印一维数组,冒泡排序,完成某个计算任务等…

类方法使用注意事项和细节讨论

1)类方法和普通方法都是随着类的加载而加载,将结构信息存储在方法区:

类方法中无this的参数

普通方法中隐含着this的参数

2)类方法可以通过类名调用,也可以通过对象名调用。

3)普通方法和对象有关,需要通过对象名调用,比如 对象名.方法名(参数) ,不能通过类名调用。

4)类方法中不允许使用和对象有关的关键字,比如this和super。普通方法(成员方法)可以。

5)类方法(静态方法)中只能访问静态变量或静态方法。

6)普通成员方法,既可以访问普通变量(方法),也可以访问静态变量(方法)。

小结:

静态方法,只能访问静态的成员。

非静态的方法,可以访问静态成员和非静态成员(必须遵守访问权限)

mian方法

深入理解main方法

public static void main(String[] args){}

1.mian方法是java虚拟机调用的。

1.java虚拟机需要调用类的main()方法,所以该方法的访问权限必须是public

2.java虚拟机在执行main()方法时不必创建对象,所以该方法必须是static

3.该方法接收String类型的数组参数,该数组中保存执行java命令时传递给所运行的类的参数。

4.java执行的程序 参数1 参数2 参数3

特别提醒

1)在main()方法中,我们可以直接调用main方法所在类的静态方法或静态属性。

2)但是,不能直接访问该类中的非静态成员,必须创建该类的一个实例对象后,才能通过这个对象去访问类中的非静态成员

3.idea中也可以运行,运行参数在下图添加后运行:

image-20211031160917847

image-20211031160719693

代码块/static代码块

基本介绍

1.代码化块又称为初始化块,属于类中的成员[即是类的一部分],类似于方法,将逻辑语句封装在方法体中,通过{}包围起来。

2.但和方法不同,没有方法名,没有返回,没有参数,只有方法体,而且不用通过对象或类显式调用,而是加载类时,或创建对象时隐式调用。

基本语法

 [修饰符]{
     代码
 };

注意:
1)修饰符可选,要写的话,也只能写static

2)代码块分为两类,使用static修饰的叫静态代码块,没有static修饰的,叫普通代码块。

3)逻辑语句可以为任何逻辑语句(输入、输出、方法调用、循环、判断等)

4);号可以写上,也可以省略。

理解

1)相当于另外一种形式的构造器(对构造器的补充机制),可以做初始化的操作

2)场景:如果多个构造器中都有重复的语句,可以抽取到初始化块中,提高代码的重用性

代码块使用注意事项和细节讨论

1.static代码块也叫静态代码块,作用就是对类进行初始化,而且它随着类的加载而执行,并且只会执行一次如果是普通代码块,每创建一个对象,就执行

2)类什么时候被加载

①创建对象实例时(new)

②创建子类对象实例,父类也会被加载

③使用类的静态成员时(静态属性,静态方法)

④类加载只会加载一次,不会重复加载

3)普通的代码块,在创建对象实例时,会被隐式的调用。

被创建一次,就会调用一次。

如果只是使用类的静态成员时,普通代码块并不会执行。

小结:

1.static代码块是类加载时,执行,只会执行一次

2.普通代码块是在创建对象时调用的,创建一次,调用一次

4)创建一个对象时,在一个类调用顺序是:(重点,难点)∶
①调用静态代码块和静态属性初始化(注意:静态代码块和静态属性初始化调用的优先级一样,如果有多个静态代码块和多个静态变量初始化,则按他们定义的顺序调用)

②调用普通代码块和普通属性的初始化(注意:普通代码块和普通属性初始化调用的优先级一样,如果有多个普通代码块和多个普通属性初始化,则按定义顺序调用)

③调用构造方法。

5)构造方法(构造器)的最前面其实隐含了super()和调用普通代码块,新写一个类演示[截图+说明],静态相关的代码块,属性初始化,在类加载时,就执行完毕,因此是优先于构造器和普通代码块执行的

6)我们看一下创建一个子类时(继承关系),他们的静态代码块,静态属性初始化,普通代码块,普通属性初始化,构造方法的调用顺序如下:
①父类的静态代码块和静态属性(优先级一样,按定义顺序执行)

②子类的静态代码块和静态属性(优先级一样,按定义顺序执行)

③父类的普通代码块和普通属性初始化(优先级一样,按定义顺序执行)

④父类的构造方法

⑤子类的普通代码块和普通属性初始化(优先级一样,按定义顺序执行)

⑥子类的构造方法

package com.gx.test;

public class Test {
    public static void main(String[] args) {
        //(1) 进行类的加载
        //1.1 先加载 父类 A02 2.2 再加载 子类 B02
        //(2) 创建对象
        //2.1 从子类构造器开始
        new B02();//对象
    }
}

class A02{//父类
    private static int n2 = getVal01();
    static {
        System.out.println("A02的一个静态代码块");//(2)
    }
    {
        System.out.println("A02的第一个普通代码块");//(5)
    }
    public int n3 = getVal02();

    private static int getVal01() {
        System.out.println("getVal01");//(1)
        return 10;
    }
    private int getVal02() {
        System.out.println("getVal02");//(6)
        return 10;
    }
    public A02(){
        //隐藏了
        //super()
        //普通代码块的普通属性的初始化。。。。。
        System.out.println("A02的构造器");//(7)
    }

}

class B02 extends A02{
    private static int n3 = getVal03();
    static {
        System.out.println("B02的一个静态代码块");//(4)
    }
    public int n4 = getVal04();
    {
        System.out.println("B02的第一个普通代码块");//(9)
    }
    private static int getVal03() {
        System.out.println("getVal03");//(3)
        return 10;
    }
    private int getVal04() {
        System.out.println("getVal04");//(8)
        return 10;
    }
    public B02(){
        //隐藏了
        //super()
        //普通代码块的普通属性的初始化。。。。。
        System.out.println("B02的构造器");//(10)
    }
}

7)静态代码块只能直接调用静态成员(静态属性和静态方法),普通代码块可以调
用任意成员。

设计模式

什么是设计模式

1.静态方法和属性的经典使用

2.设计模式是在大量的实践中总结和理论化之后优选的代码结构、编程风格、以及解决问题的思考方式。设计模式就像是经典的棋谱,不同的棋局,我们用不同的棋谱,免去我们自己再思考和摸索

什么是单例模式

1.所谓类的单例设计模式,就是采取一定的方法保证在整个的软件系统中,对某个类只能存在一个对象实例,并且该类只提供一个取得其对象实例的方法
2.单例模式有两种方式:1)饿汉式 2)懒汉式

步骤如下:
1)构造器私有化 => 防止直接new

2)类的内部创建对象

3)向外暴露一个静态的公共方法。

饿汉式

//有一个类 GirlFriend
//只能有一个女朋友
class GirlFgiend {
    private String name;
	//为了能够在静态方法中,返回gf对象,需要将其修饰为static
    //对象,通常是重量级的对象,且饿汉式可能造成创建了对象,但是没有使用
    private static GirlFriend gf= new GirlFriend("小红红");//如何保障我们只能创建一个 GirlFriend对象
	//步骤[单列模式--饿汉式]
    //1。将构造器私有化
    //2.在类的内部直接创建对象(该对象的static)
    //3.提供一个公共的static方法,返回 gf对象
    private GirlFriend(String name) {
        this.name = name;
    }
    
    public static GirlFriend getInstance() {
        return gf;
    }

}

懒汉式

//希望在程序运行过程中,只能创建一个Cat对象
//使用单例模式
class Cat {
    private String name;
    private String name;
    private static Cat cat ;
    //步骤[单列模式--懒汉式]
    //1.仍然将造器私有化
    //2.定义一个static静态属性方法
    //3.提供一个public的static方法,可以返回一个Cat对象
    //4.懒汉式,只有当用户使用getInstance时,才返回cat对象,后面再次调用时,会返回上次创建的cat对象
    //	从而保证了单列
    private Cat(String name) {
        this.name = name;
    }
    public static Cat getInstance() {
        if(cat == null) {//如果还没有创建cat对象
            cat = new Cat("小可爱");
        }
        return cat;
    }
}


饿汉式VS懒汉式

1.二者最主要的区别在于创建对象的时机不同:饿汉式是在类加载就创建了对象实例,而懒汉式是在使用时才创建。

2.饿汉式不存在线程安全问题,懒汉式存在线程安全问题。

3.饿汉式存在浪费资源的可能。因为如果程序员一个对象实例都没有使用,那么饿汉式创建的对象就浪费了,懒汉式是使用时才创建,就不存在这个问题。

4.在我们javaSE标准类中,java.lang.Runtime就是经典的单例模式。

小结

1.单例模式的两种实现方式: ⑴饿汉式 (2)懒汉式

2.饿汉式的问题:在类加载时候就创建,可能存在资源浪费问题

3.懒汉式的问题:线程安全问题

final关键字

基本介绍

final中文意思:最后的,最终的.

final可以修饰类、属性、方法和局部变量.

在某些情况下,程序员可能有以下需求,就会使用到final:

1)当不希望类被继承时,可以用final修饰

2)当不希望父类的某个方法被子类覆盖/重写(override)时,可以用final关键字修饰

访问修饰符 final 返回类型 方法名

3)当不希望类的的某个属性的值被修改,可以用final修饰

public final double TAX_RATE = 0.08

4)当不希望某个局部变量被修改,可以使用final修饰

final double TAX_RATE = 0.08 //这时 TAX_RATE 被称为局部常量

final使用注意事项和细节讨论

1)final修饰的属性又叫常量,一般用xx xx _Xx来命名

2)final修饰的属性在定义时,必须赋初值,并且以后不能再修改,赋值可以在如下位置之一[选择一个位置赋初值即可]:

​ ①定义时:如public final double TAX_RATE=0.08;

​ ②在构造器中

​ ③在代码块中

class AA{
    public final double TAX_RATE = 0.08;
    public final double TAX_RATE2 ;
    public final double TAX_RATE3 ;
    public AA() {
        TAX_RATE2 = 1.1;
    }
    {
        TAX_RATE3 = 8.8;
    }
}

3)如果final修饰的属性是静态的,则初始化的位置只能是

​ ①定义时

​ ②在静态代码块

注:但不能在构造器中赋值(防止类加载了但是构造器没有调用的情况出现,这时就没有给final修饰的属性赋值,显然是不行的)

4)final类不能继承,但是可以实例化对象。

5)如果类不是final类,但是含有final方法,则该方法虽然不能重写,但是可以被继承。

6)一般来说,如果一个类已经是final类了,就没有必要再将方法修饰成final方法。(既然已经不能被继承了,就肯定不能被重写了)

7)final不能修饰构造方法(即构造器)

8)final和static往往搭配使用,效率更高,不会导致类加载,底层编译器做了优化处理。

class Test{
    public static void main(String[] args) {
        system.out.println(BBB.num);//结果:100 所以显示类没有加载
    }
}

class BBB{
    public static final int num=100;
    static{
        System.out.println("BBB 静态代码块被执行");
    }
}

8)包装类(Integer,Double,Float,Boolean等都是final),String也是final类。

抽象类

当父类的一些方法不能确定时,可以用abstract关键字来修饰该方法,这个方法就是抽象方法,用abstract 来修饰该类就是抽象类。

抽象类的介绍

1)用abstract关键字来修饰一个类时,这个类就叫抽象类

访问修饰符 abstract 类名{
}

2)用abstract关键字来修饰一个方法时,这个方法就是抽象方法

访问修饰符 abstract 返回类型方法名(参数列表);//没有方法体

3)抽象类的价值更多作用是在于设计,是设计者设计好后,让子类继承并实现抽象类()

4)抽象类,是考官比较爱问的知识点,在框架和设计模式使用较多

抽象类使用的注意事项和细节讨论

1)抽象类不能被实例化

2)抽象类不一定要包含abstract方法。也就是说,抽象类可以没有abstract方法

3)一旦类包含了abstract方法,则这个类必须声明为abstract

4)abstract 只能修饰类和方法,不能修饰属性和其它的。

5)抽象类可以有任意成员【因为抽象类还是类】,比如:非抽象方法、构造器、静态属性等等

6)抽象方法不能有主体,即不能实现

7)如果一个类继承了抽象类,则它必须实现抽象类的所有抽象方法,除非它自己也声明为abstract类。

8)抽象方法不能使用private、final和 static来修饰,因为这些关键字都是和重写相违背的

注:static的本意是为了能够让类直接调用,但是没有方法体的方法显然没法调用,所以抽象方法不能使用static来修饰

模板模式

public class TestTemplate {
    public static void main(String[] args) {
        AA aa = new AA();
        aa.calculateTime();
        
        BB bb = new BB();
        bb.calculateTime();
    }
}
abstract public class Template {
    //抽象类-模板设计模式
    public abstract void job();//抽象方法
    public void calculateTime() {//实现方法,调用job方法
        //得到开始的时间
        long start = System.currentTimeMillis();
        job();//动态绑定机制
        //得的结束的时间
        long end = System.currentTimeMillis();
        System.out.println("AA执行时间" +(end - start));
    }
}
public class AA extends Template{
    @Override
	public void job() {//实现Template的抽象方法job
		long num = ;num: 1
		for (long i= 1; i <= 800000; i++) {
			num += i;
		}
	}
}


public class BB extends Template{
	public void job() {//这里也去,重写了Template的job方法
        long num = 0;
        for (long i = 1; i <=80000; i++) {
        	num *= i;
		}
	}
}

接口

基本介绍

接口就是给出一些没有实现的方法,封装到一起,到某个类要使用的时候,在根据具体情况把这些方法写出来。

interface 接口名{
    //属性
    //方法
}
class 类名 implements 接口{
    //自己属性;
    //自己方法;
    //必须实现的接口的抽象方法
}

小结

1.在Jdk7.0前接口里的所有方法都没有方法体,即都是抽象方法。

2.Jdk8.0后接口可以有静态方法,默认方法,也就是说接口中可以有方法的具体实现,但是需要使用default关键字修饰

//在jdk8后,可以有默认实现方法,需要使用default关键字修饰
default public void ok() {
	System.out.println("ok ...");
}
//在jdk8后,可以有静态方法
public static void cry() {
	System.out.println( "cry ....");
}

注意事项和绍节

1)接口不能被实例化

2)接口中所有的方法是 public方法,接口中抽象方法,可以不用abstract,因为方法默认的就为abstract

3)一个普通类实现接口,就必须将该接口的所有方法都实现。

4)抽象类实现接口,可以不用实现接口的方法。

5)一个类同时可以实现多个接口

interface IB {
	void hi();
}
interface IC {
	void say(;
}
class Pig implements IB, IC {
}

6)接口中的属性,只能是final的,而且是 public static final 修饰符。

比如:int a=1;实际上是public static final int a=1;(必须初始化)

7)接口中属性的访问形式:接口名.属性名

8)一个接口不能继承其它的类,但是可以继承多个别的接口

 interface A extends B,C{}

9)接口的修饰符只能是public和默认,这点和类的修饰符是一样的。

接口vs继承

接口和继承解决的问题不同

继承的价值主要在于:解决代码的复用性和可维护性。

接口的价值主要在于:设计,设计好各种规范(方法),让其它类去实现这些方法。即更加的灵活

接口比继承更加灵活

接口比继承更加灵活,继承是满足is - a的关系,而接口只需满足 like - a的关系。

接口在一定程度上实现代码解耦

即:接口规范性+动态绑定机制

接口的多态特性

1)多态参数
在前面的Usb接口案例,Usb usb,既可以接收手机对象,又可以接收相机对象,就体现了接口多态(接口引用可以指向实现了接口的类的对象)

public class InterfacePolyParameter {
    public static void main(String[] args) {
        //接口的多态体现
        //接口类型的变量if01 可以指向实现了IF接口类的对象实例
        IF if01 =new Monster();
        if01 = new Car();
        //继承体现的多态
        AAA a = new BBB();
        a = new CCC();

    }
}
interface IF {}
class Monster implements IF{}
class Car implements IF{}

class AAA {}
class BBB extends AAA{}
class ccc extends AAA{}

2)多态数组

public class InterfacePolyArr {
    public static void main(String[] args) {
        //多态数组->接口类型数组
        Usb[] usbs = new Usb[2];
        usbs[o] = new Phone_();
        usbs[1] = new Camera_();
        for(int i = 0; i < usbs.length; i++) {
            usbs[i].work();//动态绑定..
            //和前面一样,我们仍然需要进行类型的向下转型
            if(usbs[i] instanceof Phone_) {//判断他的运行类型是
                ((Phone_) usbs[i]).call();
            }
        }
    }
}

interface Usb{
    void work();
}

class Phone_ implements Usb {
    public void call() {
        System.out.println("手机可以打电话。..");
    }
    @0verride
    public void work() {
        system.out.println("手机工作中...");
    }
}

class Camera_ implements Usb {
    @0verride
    public void work() {
        system.out.println("相机工作中...");
    }
} 

3)接口存在多态传递

public class InterfacePolyPass {
    public static void main(String[] args) {
        //接口类型的变量可以指向,实现了该接口的类的对象实例
        IG ig = new Teacher();
        //如果IG 继承了IH接口,而Teacher类实现了IG接口
        //那么,实际上就相当于 Teacher类也实现了IH接口
        //这就是所谓的接口多态多态传递现象
        IH ih = new Teacher();

    }
}
interface IH {
    void hi();
}
interface IG extends IH{}
class Teacher implements IG {
    @0verride
    public void hi(){  
    }
}

四种内部类

一个类的内部又完整的嵌套了另一个类结构。被嵌套的类称为内部类(inner class),嵌套其他类的类称为外部类(outer class)。是我们类的第五大成员**[属性、方法、构造器、代码块、内部类]**,内部类最大的特点就是可以直接访问私有属性,并且可以体现类与类之间的包含关系

基本语法

class Outer{//外部类
    class Inner{//内部类
    }
}
class Other{//外部其他类
}

内部类的分类

定义在外部类局部位置上(比如方法内):

​ 1)局部内部类(有类名)
​ 2)匿名内部类(没有类名,重点)

定义在外部类的成员位置上:

​ 1)成员内部类(没用static修饰)

​ 2)静态内部类(使用static修饰)

局部内部类的使用

说明:局部内部类是定义在外部类的局部位置,比如方法中,并且有类名。

1.可以直接访问外部类的所有成员,包含私有的

2.不能添加访问修饰符,因为它的地位就是一个局部变量。局部变量是不能使用修饰符的。但是可以使用final修饰,因为局部变量也可以使用final

3.作用域;仅仅在定义它的方法或代码块中。

4.局部内部类—访问---->外部类的成员[访问方式:直接访问]

5.外部类—访问---->局部内部类的成员

访问方式:创建对象,再访问(注意:必须在作用域内)

6.外部其他类—不能访问----->局部内部类(因为局部内部类地位是一个局部变量)

7.如果外部类和局部内部类的成员重名时,默认遵循就近原则,如果想访问外部类的E员,则可以使用(外部类名.this.成员)去访问
System.out.printIn("外部类的n2=”+外部类名.this.n2);

public class LocalInnerClass {
    public static void main(String[] args) {
        Outer02 outer02 = new Outer02();
        outer02.m1();
    }
}

class Outer02 {//外部类
    private int n1 = 100;

    private void m2() {
    }//私有方法

    public void m1() {
        //1。局部内部类是定义在外部类的局部位置,通常在方法
        //3.不能添加访问修饰符,但是可以使用final修饰
        //4.作用域:仅仅在定义它的方法或代码块中
        final class Inner02 {//局部内部类(本质仍然是一个类)
            //2.可以直接访问外部类的所有成员,包含私有的
            private int n1 = 800;

            public void f1() {
                //5.局部内部类可以直接访问外部类的成员,比如下面外部类n1 和 m2()
                //Outer02.this本质就是外部类的对象,即哪个对象调用了m1, Outer02.this就是哪个对象
                System.out.println("n1=" + n1 + "外部类的n1=" + Outer02.this.n1);
                m2();
            }
        }
        //6.外部类在方法中,可以创建Inner02对象,然后调用方法即可
        //7.如果外部类和局部内部类的成员重名时,默认遵循就近原则,如果想访问外部类的成员,使用 外部类名.this.成员 去访问
        Inner02 inner02 = new Inner02();
        inner02.f1();
    }
    
}


记住:

(1)局部内部类定义在方法中/代码块

(2)作用域在方法体或者代码块中

(3)本质仍然是一个类

匿名内部类的使用(重要!!!)

说明:匿名内部类是定义在外部类的局部位置,比如方法中,并且没有类名

(1)本质是类(2)内部类(3)该类没有名字(4)同时还是一个对象

1.匿名内部类的基本语法

new 类或接口(参数列表){
    类体
};

具体例子:

public class AnonymousInnerClass {
    public static void main(String[] args) {
        Outer04 outer04 = new Outer04();
        outer04.method();
    }
}

class Outer04 {//外部类
    private int n1 = 10;//属性
    public void method() {//方法
        //1.需求:想使用IA接口,并创建对象
        //2.传统方式,是写一个类,实现该接口,并创建对象
        //IA tiger = new Tiger();
        //tiger.cry();
        //3.需求是 Tiger/Dog 类只是使用一次,后面再不使用
        //4.可以使用匿名内部类来简化开发
        //5. tiger的编译类型? IA
        //6. tiger的运行类型? 就是匿名内部类 Outer04$1
        /*
            我们看底层 会分配类名 Outer04$1
            class outer04$ implements IA {
                @Override
                public void cry() {
                    System.out.println("老虎叫唤...");
                }
            }
		*/
        //7. jdk底层在创建匿名内部类 Outer04$1,立即马上就创建了 Outer04$1 实例,并且把地址返回给 tiger
        //8.匿名内部类使用一次,就不能再使用
        IA tiger = new IA() {
            @Override
            public void cry() {
                System.out.println("老虎叫唤...");
            }
        };
        System.out.println("tiger的运行类型=" + tiger.getClass());
        tiger.cry();


        //演示基于类的匿名内部类
        //分析
        //1. father编译类型 Father
        //2. father运行类型 Outer04$2
        //3.底层会创建匿名内部类
        /*
        	class Outer04$2 extends Father{
        		@Override
        		public void test() {
        			System.out.printLn("匿名内部类重写了test方法");
        		}
        	}
        */
        //4.同时也直接返回了匿名内部类 Outer04$2的对象
        //5.注意("jack")参数列表会传递给构造器
        Father father =new Father( "jack"){
            @Override
            public void test() {
                System.out.println("匿名内部类重写了test方法");
            }
        };
        System.out.println("father对象的运行类型=" + father.getClass());//Outer04$2
        father.test();

        //基于抽象类的匿名内部类
        Animal animal = new Animal(){
            @Override
            void eat() {
                System.out.println("小狗吃骨头...");
            }
        };
        animal.eat();
    }
}

interface IA {//接口
    public void cry();
}

//class Tiger implements IA {
//    @Override
//    public void cry() {
//        System.out.println("老虎叫唤...");
//    }
//}
//class Dog implements IA{
//    @Override
//    public void cry() {
//        System.out.println("小狗汪汪...");
//    }
//}


class Father {//类
    public Father(String name) {//构造器
        System.out.println("接收到name" + name);
    }
    public void test() {//方法

    }
}
abstract class Animal {//抽象类
    abstract void eat();
}

2.匿名内部类的语法比较奇特,请大家注意,因为匿名内部类既是一个类的定义,同时它本身也是一个对象,因此从语法上看,它既有定义类的特征,也有创建对象的特征,对前面代码分析可以看出这个特点,因此可以调用匿名内部类方法。

3.可以直接访问外部类的所有成员,包含私有的

4.不能添加访问修饰符,因为它的地位就是一个局部变量。

5.作用域:仅仅在定义它的方法或代码块中。

6.匿名内部类—访问---->外部类成员[访问方式:直接访问]

7.外部其他类—不能访问----->匿名内部类(因为匿名内部类地位是一个局部变量)

8.如果外部类和匿名内部类的成员重名时,内部类访问的话,默认遵循就近原则,如果想访问外部类的成员,则可以使用(外部类名.this.成员)去访问

public class AnonymousInnerClassDetail {
    public static void main(String[] args) {
        Outer05 outer05 = new 0uter05();
        outer05.f1();
		//外部其他类---不能访问----->匿名内部类(因为匿名内部类地位是一个局部变量)
        System.out.println("main outer05 hashcode=" + outer05);
    }
}

class Outer05 {
    private int n1 = 99;
    public void f1() {
        //创建一个基于类的匿名内部类
        //不能添加访问修饰符,因为它的地位就是一个局部变量
        //作用域:仅仅在定义它的方法或代码块中
        Person p = new Person(){
            private int n1 = 88;
            @0verride
            public void hi() {
                //可以直接访回外部类的所有成员,包含私有的
                //默认遵循就近原则,如果想访问外部类的成员,则可以使用(外部类名.this.成员)去访问
                System.out.println("匿名内部类重写了 hi方法 n1=" + n1 + "外部内的n1=" + Outer05.this.n1);
                //outer05.this就是调用f1的对象
                System.out.println("Outer05.this hashcode=" + Outer05.this.n1);

            }
        };
        p.hi();//动态绑定,运行类型是 Outer05$1
        
        //也可以直接调用,匿名内部类本身也是返回对象
        // class匿名内部类 extends Person{}
        new Person(){
            @Ovarride
            public void hi() {
                System.out.println("匿名内部类重写了 hi方法,哈哈...");
            }
            @Override
            public void ok(String str) {
                super.ok(str);
            }
        }.ok( "jack");//.hi();

    }
}

class Person {//类
    public void hi() {
        system.out.println("Person hi()");
    }
    public void ok(String str) {
        System.out.println("Person ok()" + str);
    }
}

匿名内部类的最佳实践

public class InnerClassExercise01 {
    public static void main(String[] args) {
        //当做实参直接传递,简洁高效
        f1(new,IL() {
            @0verride
            public void show() {
                System.out.println("这是一副名画~... ");
            }
        });
        //传统方法
        f1(new Picture());
    }
    //静态方法,形参是接口类型
    public static void f1(IL il) {
        il.show();
    }
}

//接口
interface IL {
    void show();
}
//类->实现IL => 编程领域(硬编码)
class Picture implements IL {
    @Override
    public void show() {
        System.out.println("这是一副名画...");
    }
}
public class InnerClassExercise02 {
    public static void main(String[] args) {
        /*
        1.有一个铃声接口Bell,里面有个ring方法。(右图)
        2.有一个手机类Cellphone,具有闹钟功能alarmClock,参数是Bell类型(右图)
        3.测试手机类的闹钟功能,通过匿名内部类(对象)作为参数,打印:懒猪起床了
        4.再传入另一个匿名内部类(对象),打印:小伙伴上课了
        */
        //老韩解读
        //1.传递的是实现了 Bell接口的匿名内部类 InnerClassExercise02$1
        //2.重写了ring
        //3.Bell bell = new Bell() {
        //    @Override
        //    public void ring() {
        //        System.out.println("懒猪起床了");
        //    }
        //};

        CellPhone cellPhone = new CellPhone();
        cellPhone.alarmClock(new Bell() {
            @Override
            public void ring() {
                System.out.println("懒猪起床了");
            }
        });
        cellPhone.alarmClock(new Bell() {
            @Override
            public void ring() {
                System.out.println("小伙伴上课了");
            }
        });
    }
}

interface Bell {//接口

    void ring();//法
}

class CellPhone {//类

    public void alarmClock(Bell bell) {//形参是Bell接口类型
        bell.ring();//动态绑定
    }
}

成员内部类

说明:成员内部类是定义在外部类的成员位置,并且没有static修饰。

1.可以直接访问外部类的所有成员,包含私有的

2.可以添加任意访问修饰符(public、protected、默认、private),因为它的地位就是一个成员。

3.作用域和外部类的其他成员一样,为整个类体, 比如前面案例,在外部类的成员方法中创建成员内部类对象再调用方法.

4.成员内部类—访问---->外部类(比如:属性)【访问方式:直接访问】(说明)

5.外部类—访问------>内部类(说明)访问方式:创建对象,再访问

6.外部其他类—访问---->成员内部类

7.如果外部类和内部类的成员重名时,内部类访问的话,默认遵循就近原则,如果想访问外部类的成员,则可以使用(外部类名.this.成员)去访问

public class MemberInnerClass01 {
    public static void main(String[] args) {
        Outer08 outer08 = new Outer08();
        outer08.t1();

        //外部其他类,使用成员内部类的三种方式
        //第一种方式
        //outer08.new Inner08(); 相当于把 new Inner08()当做是outer08成员(这就是一个语法,不要特别的纠结。)
        Outer08.Inner08 inner08 = outer08.new Inner08();
        inner08.say();
        //第二方式在外部类中,编写一个方法,可以返回 Inner08对象
        Outer08.Inner08 inner08Instance = outer08.getInner08Instance();
        inner08Instance.say();
        //第三种方式
        Outer08.Inner08 inner082 =new Outer08().new Inner08();
    }
}

class Outer08 {//外部类
    private int n1 = 10;
    public String name = "张三";

    private void hi() {
        System.out.println("hi()方法...");
    }

    //1.注意:成员内部类,是定义在外部内的成员位置上
    public class Inner08 {//成员内部类
        private double sal = 99.8;
        private int n1 = 66;
        public void say() {
            //可以直接访问外部类的所有成员,包含私有的
            //如果成员内部类的成员和外部类的成员重名,会遵守就近原则。可以通过 外部类名.this.属性 来访问外部类的成员
            System.out.println("n1 =" + n1 + " name = " + name + " 外部类的n1= " + Outer08.this.n1);
            hi();
        }
    }

    //方法,返回一个Inner08实例
    public Inner08 getInner08Instance() {
        return new Inner08();
    }

    //写方法
    public void t1() {
        //使用成员内部类
        //创建成员内部类的对象,然后使用相关的方法
        Inner08 inner08 = new Inner08();
        inner08.say();
        System.out.println(inner08.sal);
    }

}

静态内部类

说明:静态内部类是定义在外部类的成员位置,并且有static修饰

1.可以直接访问外部类的所有静态成员,包含私有的,但不能直接访问非静态成员

2.可以添加任意访问修饰符(public.protected、默认、private),因为它的地位就是一个成员。

3.作用域:同其他的成员,为整个类体

4.静态内部类—访问---->外部类(比如:静态属性)[访问方式:直接访问所有静态成员]

5.外部类—访问------>静态内部类访问方式:创建对象,再访问

6.外部其他类—访问----->静态内部类

7.如果外部类和静态内部类的成员重名时,静态内部类访问的时,默认遵循就近原则,如果想访问外部类的成员,则可以使用(外部类名.成员)去访问(因为是静态的,所以不用再用 外部类名.this.成员 )

public class Test {
    public static void main(String[] args) {
        Outer10 outer10 = new Outer10();
        outer10.m1();
        //外部其他类使用静态内部类
        //方式1
        //因为静态内部类,是可以通过类名直接访问(前提是满足访问权限)
        Outer10.Inner10 inner10 = new Outer10.Inner10();
        inner10.say();
        //方式二
        //编写一个方法,可以返回静态内部类的对象实例.
        Outer10.Inner10 inner101 = outer10.getInner10();
        System.out.println("===========");
        inner101.say();

        Outer10.Inner10 inner10_ = Outer10.getInner10_();
        System.out.println("*********");
        inner10_.say();


    }
}

class Outer10 {//外部类
    private int n1 = 10;
    public static String name = "张三";

    private static void cry(){}
    //Inner10就是静态内部类
    // 1.放在外部类的成员位置
    // 2.使用static修饰
    // 3.可以直接访问外部类的所有静态成员,包含私有的,但不能直接访问非静态成员
    // 4.可以添加任意访问修饰符(public、protected 、默认、private),因为它的地位就是一个成员
    // 5.作用域:同其他的成员,为整个类体
    static class Inner10 {
        public static String name = "韩顺平教育";
        public void say() {
            //如果外部类和静态内部类的成员重名时,静态内部类访问的时,默认遵循就近原则,如果想访问外部类的成员,则可以使用(外部类名.成员)去访问
            System.out.println(name+" 外部类name= "+ Outer10.name);
            cry();
        }
    }
    public void m1(){//外部类---访问------>静态内部类访问方式:创建对象,再访问
        Inner10 inner10 = new Inner10();
        inner10.say();
    }
    public Inner10 getInner10(){
        return  new Inner10();
    }
    public static Inner10 getInner10_(){
        return  new Inner10();
    }
}

小结

(1)内部类有四种:①局部内部类② 匿名内部类③成员内部类④静态内部类

(2)重点还是掌握匿名内部类使用

new/接口(参数列表){
    //...
};

(3)成员内部类,静态内部类是放在外部类的成员位置,本质就是一个成员

(4)其他细节看笔记…

枚举

基本内容

1)枚举对应英文(enumeration,简写enum)

2)枚举是一组常量的集合。

3)可以这里理解:枚举属于一种特殊的类,里面只包含一组有限的特定的对象。

枚举的两种实现方式

自定义类实现枚举

1.不需要提供setXxx方法,因为枚举对象值通常为只读.

2.对枚举对象/属性使用final + static共同修饰,实现底层优化.

3.枚举对象名通常使用全部大写,常量的命名规范.

4.枚举对象根据需要,也可以有多个属性

public class Test {
    public static void main(String[] args) {
        System.out.println(Season.SPRING);
        System.out.println(Season.AUTUMN);
    }
}

//演示自定义枚举实现
class Season {//外部类
    private String name;
    private String desc;//描述
    public final static Season SPRING = new Season("春天","温暖");
    public final static Season WINTER = new Season("冬天","寒冷");
    public final static Season AUTUMN = new Season("秋天","凉爽");
    public final static Season SUMMER = new Season("夏天","炎热");
    //1.将构造器私有化,目的防止直接 new
    //2.去掉setXxx方法,防止属性被修改
    //3.在Season内部,直接创建固定的对象
    //4.优化,可以加入 final修饰符


    public String getName() {
        return name;
    }

    public String getDesc() {
        return desc;
    }

    private Season(String name, String desc) {
        this.name = name;
        this.desc = desc;
    }
}
使用enum关键字实现枚举
public class Test {
    public static void main(String[] args) {
        System.out.println(Season2.SPRING);
        System.out.println(Season2.AUTUMN);
    }
}

//演示使用enum关键字来实现枚举
enum Season2 {//外部类
    //    public final static Season SPRING = new Season("春天","温暖");
    //    public final static Season WINTER = new Season("冬天","寒冷");
    //    public final static Season AUTUMN = new Season("秋天","凉爽");
    //    public final static Season SUMMER = new Season("夏天","炎热");

    //如果使用了enum来实现枚举类
    // 1.使用关键字 enum替代class
    // 2.public static final Season SPRING = new Season("春天","温暖")
    //  直接使用SPRING("春天","温暖") 解读 常量名(实参列表)
    // 3.如果有多个常量(对象),使用,号间隔即可
    // 4.如果使用enum来实现枚举,要求将定义常量对象,写在前面
    //5。如果我们使用的是无参构造器,创建常量对象,则可以省略()

    SPRING("春天","温暖"),WINTER("冬天","寒冷"),AUTUMN("秋天","凉爽"),
    SUMMER("夏天","炎热"),What;
    private String name;
    private String desc;//描述
    public String getName() {
        return name;
    }

    public String getDesc() {
        return desc;
    }

    private Season2() {//无参构造器
    }

    private Season2(String name, String desc) {
        this.name = name;
        this.desc = desc;
    }

    @Override
    public String toString() {
        return "Season2{" +
                "name='" + name + '\'' +
                ", desc='" + desc + '\'' +
                '}';
    }
}

enum关键字实现枚举注意事项

1.当我们使用enum关键字开发一个枚举类时,默认会继承Enum类。而且是一个final类(使用javap反编译来验证)

image-20211104163401090

2传统的public static final Season2 SPRING = new Season2(“春天”,“温暖”);简化成 SPRING(“春天”,“温暖”),这里必须知道,它调用的是哪个构造器.

3.如果使用无参构造器创建枚举对象,则实参列表和小括号都可以省略

4.当有多个枚举对象时,使用","间隔,最后有一个分号结尾

5.枚举对象必须放在枚举类的行首

枚举的常用方法

1.toString:Enum类已经重写过了,返回的是当前对象名,子类可以重写该方法,用于返回对象的属性信息

2.name:返回当前对象名(常量名),子类中不能重写

3.ordinal:返回当前对象的位置号,默认从0开始

4.values:返回当前枚举类中所有的常量

5.valueof:将字符串转换成枚举对象,要求字符串必须为已有的常量名,否则报异常!

6.compareTo:比较两个枚举常量,比较的就是编号!

package com.gx.test;

public class Test {
    public static void main(String[] args) {
        Season2 autumn = Season2.AUTUMN;
        //输出枚举对象的名字
        System.out.println(autumn.name());
        // ordinal()输出的是该枚举对象的次序/编号,从0开始编号
        //AUTUMN 枚举对象是第三个,因此输出2
        System.out.println(autumn.ordinal());
        //从反编译可以看出values方法,返回Season2[]
        // 含有定义的所有枚举对象
        System.out.println( "===遍历取出枚举对象(增强for)====");
        Season2[] valuse = autumn.values();
        for (Season2 season:valuse) {
            System.out.println(season);
        }

        //valueOf:将字符串转换成枚举对象,要求字符串必须为已有的常量名,否则报异常
        // 执行流程
        // 1。根据你输入的 "AUTUMN" 到Season2的枚举对象去查找
        // 2.如果找到了,就返回,如果没有找到,就报错
        Season2 autumn1 = Season2.valueOf("AUTUMN ");
        System.out.println( "autumn1=" +autumn1);

        //compareTo:比较两个枚举常量,比较的就是编号
        // 1.就是把Season2.AUTUNN枚举对象的编号和Season2.SUMMER枚举对象的编号比较
        /*源码
        public final int compareTo(E o) {
            Enum<?> other = (Enum<?>)o;
            Enum<E> self = this;
            if (self.getClass() != other.getClass() && // optimization
                    self.getDeclaringClass() != other.getDeclaringClass())
                throw new ClassCastException();
            return self.ordinal - other.ordinal;
            //Season2.AUTUMN的编号[2] - Season2.SUMMER的编号[3] //等于-1
        }
        */
        System.out.println(Season2.AUTUMN.compareTo(Season2.SUMMER));

    }
}

//演示使用enum关键字来实现枚举
enum Season2{//外部类
    SPRING("春天","温暖"),
    SUMMER("夏天","炎热"),
    WINTER("冬天","寒冷"),
    AUTUMN("秋天","凉爽"),/*What*/;
    private String name;
    private String desc;//描述
    public String getName() {
        return name;
    }

    public String getDesc() {
        return desc;
    }

    private Season2() {//无参构造器
    }

    private Season2(String name, String desc) {
        this.name = name;
        this.desc = desc;
    }

    @Override
    public String toString() {
        return "Season2{" +
                "name='" + name + '\'' +
                ", desc='" + desc + '\'' +
                '}';
    }
}

enum的注意试想和细节讨论

1)使用enum关键字后,就不能再继承其它类了,因为enum会隐式继承Enum,而Java是单继承机制。

2)枚举类和普通类一样,可以实现接口,如下形式。

enum 类名 implements 接口1,接口2{}

注解(Annotation)

1)注解(Annotation)也被称为元数据(Metadata),用于修饰解释包、类、方法、属性、构造器、局部变量等数据信息。

2)和注释一样,注解不影响程序逻辑,但注解可以被编译或运行,相当于嵌入在代码中的补充信息。

3)在JavaSE中,注解的使用目的比较简单,例如标记过时的功能,忽略警告等。在JavaEE中注解占据了更重要的角色,例如用来配置应用程序的任何切面,代替java EE旧版中所遗留的繁冗代码和XML配置等。

使用Annotation时要在其前面增加@符号,并把该 Annotation当成一个修饰符使用。用于修饰它支持的程序元素

三个基本的Annotation

1)@Override:限定某个方法,是重写父类方法,该注解只能用于方法

2)@Deprecated: 用于表示某个程序元素(类,方法等)已过时

3)@SuppressWarnings: 抑制编译器警告

补充说明: @interface的说明 interface不是interface,是注解类是jdk1.5之后加入的

@Override

1.@Override表示指定重写父类的方法(从编译层面验证),如果父类没有fly方法,则会报错

2.如果不写@Override注解,而父类仍有public void fly0)0,仍然构成重写

3.@Override只能修饰方法,不能修饰其它类,包,属性等等

4.查看@Override注解源码为@Target(ElementType.METHOD),说明只能修饰方法

5.@Target是修饰注解的注解,称为元注解

package com.gx.test;

public class Test {
    public static void main(String[] args) {
    }
}

class Father {
    public void fly() {
        System.out.println("Father fly...");
    }
}

class Son extends Father {//子类

    // 1.@Override 注解放在fly方法上,表示子类的fly方法时重写了父类的fly
    // 2.这里如果没有写 @Override 还是重写了父类fLy
    // 3.如果你写了@Override注解,编译器就会去检查该方法是否真的重写了父类的方法,如果的确重写了,则编译通过,如果没有构成重写,则编译错误
    // 4.@Override源码
    //  如果发现@interface表示一个注解类
        /*
            @Target(ElementType.METHOD)
            @Retention(RetentionPolicy.SOURCE)
            public @interface Override {
            }
        */
    @Override//说明
    public void fly() {
        System.out.println("Son fly....");
    }
}

@Deprecated

1.用于表示某个程序元素(类,方法等)已过时

2.可以修饰方法,类,字段,包,参数等等

3.@Target(value={CONSTRUCTOR, FIELD, LOCAL_VARIABLE, METHOD,PACKAGE, PARAMETER, TYPE})

4.@Deprecated的作用可以做到新旧版本的兼容和过渡

package com.gx.test;

public class Test {
    public static void main(String[] args) {
        A a = new A();
        a.hi();
        System.out.println(a.n1);
    }
}

// 1.@Deprecated 修饰某个元素,表示该元素已经过时
// 2.即不在推荐使用,但是仍然可以使用
// 3.@Documented源码
    /*
        @Documented
        @Retention(RetentionPolicy.RUNTIME)
        @Target(value={CONSTRUCTOR, FIELD, LOCAL_VARIABLE, METHOD, PACKAGE, MODULE, PARAMETER, TYPE})
        public @interface Deprecated {
            String since() default "";
            boolean forRemoval() default false;
        }
    */
// 4.可以修饰方法,类,字段,包,参数等等
// 5.@Deprecated 可以做版本升级过渡使用

@Deprecated
class A {
    @Deprecated
    public int n1 = 10;

    @Deprecated
    public void hi() {
    }
}

@SuppressWarnings

各种值的说明

1)unchecked是忽略没有检查的警告

2)rawtypes是忽略没有指定泛型的警告(传参时没有指定泛型的警告错误)

3)unused是忽略没有使用某个变量的警告错误

4)@SuppressWarnings可以修饰的程序元素为,查看@Target

5)生成@SupperssWarnings时,不用背,直接点击左侧的黄色提示,就可以选择(注意可以指定生成的位置)

package com.gx.test;

import java.util.ArrayList;
import java.util.List;

//@SuppressWarnings({"rawtypes","unchecked","unused"})
public class Test {
    //1.当我们不希望看到这些警告的时候,可以使用 SuppressWarnings 注解来抑制警告信息
    // 2。在{""}中,可以写入你希望抑制(不显示)警告信息
    //3。可以指定的警告类型有
//        all (抑制所有警告)
//        boxing (抑制装箱、拆箱操作时候的警告)
//        cast (抑制映射相关的警告)
//        dep-ann (抑制启用注释的警告)
//        deprecation (抑制过期方法警告)
//        fallthrough (抑制确在switch中缺失breaks的警告)
//        inally (抑制finally模块没有返回的警告)
//        hiding ()
//        incomplete-switch  (enum case)(忽略没有完整的switch语句)
//        nls (忽略非nls格式的字符)
//        null (忽略对null的操作)
//        rawtypes (使用generics时忽略没有指定相应的类型)
//        serial class (忽略在serializable类中没有声明serialVersionUID变量)
//        static-access (抑制不正确的静态访问方式警告)
//        synthetic-access (抑制子类没有按最优方法访问内部类的警告)
//        unchecked (抑制没有进行类型检查操作的警告)
//        unqualified-field-access (抑制没有权限访问的域的警告)
//        unused (抑制没被使用过的代码的警告)
    //4.关于SuppressWarnings作用范围是和你放置的位置相关
    //比如@SuppressWarnings放置在 main方法,那么抑制警告的范围就是 main
    // 通常我们可以放置具体的语句,方法,类.
    // @SuppressWarnings源码
    // (1)放置的位置就是 TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE, MODULE
    // (2)该注解类有数组String[] values()设置一个数组比如 {"rawtypes","unchecked","unused"}
        /*
            @Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE, MODULE})
            @Retention(RetentionPolicy.SOURCE)
            public @interface SuppressWarnings {
                String[] value();
            }
        */

    @SuppressWarnings({"rawtypes","unchecked","unused"})
    public static void main(String[] args) {
        List list = new ArrayList();
        list.add("jack");
        list.add("tom");
        list.add("mary");
        int i;
        System.out.println(list.get(1));
    }
    public void f1(){
        @SuppressWarnings("rawtypes")
        List list = new ArrayList();
        list.add("jack");
        list.add("tom");
        list.add("mary");
        @SuppressWarnings("unused")
        int i;
        System.out.println(list.get(1));
    }
}

元注解–JDK的Annotation(了解)

元注解的基本介绍

JDK的元 Annotation 用于修饰其他 Annotation

元注解:本身作用不大,讲这个原因希望同学们,看源码时,可以知道他是干什么

元注解的种类

(使用不多,了解,不用深入研究)

1)Retention//指定注解的作用范围,三种 SOURCE,CLASS,RUNTIME

2)Target//指定注解可以在哪些地方使用

3)Documented//指定该注解是否会在javadoc体现4) Inherited//子类会继承父类注解

@Retention

说明
只能用于修饰一个 Annotation定义,用于指定该Annotation可以保留多长时间,@Rentention包含一个 RetentionPolicy类型的成员变量,,使用@Rentention时必须为该value成员变量指定值:

@Retention的三种值

1)RetentionPolicy.SOURCE:编译器使用后,直接丢弃这种策略的注解

2)RetentionPolicy.CLASS:编译器将把注解记录在class文件中.当运行Java程序时,JVM不会保留注解。这是默认值

3)RetentionPolicy.RUNTIME:编译器将把注解记录在class文件中,当运行Java程序时,JVM会保留注解.程序可以通过反射获取该注解

@Target

基本说明:

用于修饰Annotation定义,用于指定被修饰的Annotation能用于修饰哪些程席完素.

@Taraet 也包含一个名为value 的成品变量

@Documented

基本说明:

@Documented:用于指定被该元 Annotation修饰的Annotation类将被javadoc工具提取成文档,即在生成文档时,可以看到该注解。

说明:定义为Documented的注解必须设置Retention值为RUNTIME。

@lnherited注解

被它修饰的Annotation将具有继承性.如果某个类使用了被@Inherited修饰的Annotation,则其子类将自动具有该注解

说明:实际应用中,使用较少,了解即可。

异常

基本概念

Java语言中,将程序执行中发生的不正常情况称为“异常”。(开发过程中的语法错误和逻辑错误不是异常)

执行过程中所发生的异常事件可分为两类

1)Error(错误):Java虚拟机无法解决的严重问题。如:JVM系统内部错误、资源耗尽等严重情况。比如:StackOverflowError[栈溢出]OOM(out ofmemory),Error是严重错误,程序会崩溃。

2)Exception:其它因编程错误或偶然的外在因素导致的一般性问题,可以使用针对性的代码进行处理。例如空指针访问,试图读取不存在的文件,网络连接中断等等.

3)Exception 分为两大类:

​ 运行时异常[程序运行时,发生的异常]

​ 编译时异常[编程时,编译器检查出的异常]

异常体系图(一部分)

image-20211104210819655

小结

1.异常分为两大类,运行时异常和编译时异常.

2运行时异常,编译器不要求强制处置的异常。一般是指编程时的逻辑错误,是程序员应该避免其出现的异常。java.lang.RuntimeException类及它的子类都是运行时异常

3.对于运行时异常,可以不作处理,因为这类异常很普遍,若全处理可能会对程序的可读性和运行效率产生影响

4.编译时异常,是编译器要求必须处置的异常。

五大运行时异常

常见的运行时异常包括

1)NullPointerException 空指针异常

当应用程序试图在需要对象的地方使用null时,抛出该异常

String name = null;
System.out.println(name.length());

2)ArithmeticException 数学运算异常

当出现异常的运算条件时,抛出此异常。(例如除以0)

3)ArraylndexOutOfBoundsException 数组下标越界异常

用用非法索引访问数组时抛出的异常。如果索引为负或大于等于数组大小,则该索引为非法索引

int [] arr = {1,2,3};
for (int i = 0; i <= arr.length; i++) {//<=则取了a[3]数组越界
    System.out.println(arr[i]);
}

4)ClassCastException 类型转换异常

当试图将对象强制转换为不是实例的子类时,抛出该异常。

public class Test {
    public static void main(String[] args) {
        A b = new B();//向上转型
        B b2 = (B) b;//向下转型
        C c2 = (C) b;///这里抛出ClassCastException
    }
}

class A {}
class B extends A { }
class C extends A { }

5)NumberFormatException 数字格式不正确异常门

当应用程序试图将字符串转换成一种数值类型,但该字符串不能转换为适当格式时,抛出该异常=>使用异常我们可以确保输入是满足条件数字

String name ="韩顺平教育";//将String转成int
int num = Integer.parseInt(name);//抛出NumberFormatException
System.out.println(num);//

编译异常

编译异常是指在编译期间,就必须处理的异常,否则代码不能通过编译。

常见的编译异常

SQLException//操作数据库时,查询表可能发生异常

IOException//操作文件时,发生的异常

FileNotFoundException//当操作一个不存在的文件时,发生异常

ClassNotFoundException //加载类,而该类不存在时,异常

EOFException//操作文件,到文件未尾,发生异常

lllegalArguementException//参数异常

异常处理

基本介绍

异常处理就是当异常发生时,对异常处理的方式。

异常处理的方式

1)try-catch-finally
程序员在代码中捕获发生的异常,自行处理

try {
    //代码//可能有异常
} catch (Exception e) {
    // 捕获到异常
    // 1.当异常发生时
    // 2.系统将异常封装成 Exception对象e,传递给catch
    // 3.得到异常对象后,程序员,自己处理
    // 4.注意,如果没有发生异常catch代码块不执行

} finally {
    // 1.不管try代码块是否有异常发生,始终要执行finally 
    // 2.所以,通常将释放资源的代码,放在finally
}

2)throws
将发生的异常抛出,交给调用者(方法)来处理,最顶级的处理者就是JVM

throws处理机制图
1.try-catch-finally和throws二选一
2.如果程序员,没有显示是处理异常,默认throws

image-20211104214609208

try-catch异常处理

1)Java提供try和catch块来处理异常。try块用于包含可能出错的代码。catch块用于处理try块中发生的异常。可以根据需要在程序中有多个数量的try…catch块。

2)基本语法

try {
//可疑代码
//将异常生成对应的异常对象,传递给catch块
}catch(异常){
//对异常的处理
}
//如果没有finally,语法是可以通过

try-catch异常处理细节

1)如果异常发生了,则异常发生后面的代码不会执行,直接进入到catch块.

2)如果异常没有发生,则顺序执行try的代码块,不会进入到catch.

3)如果希望不管是否发生异常,都执行某段代码(比如关闭连接,释放资源等) 则使用如下代码- finally {}

4)可以有多个catch语句,捕获不同的异常(进行不同的业务处理),要求父类异常在后,子类异常在前,比如(Exception在后,NullPointerException在前),如果发生异常,只会匹配一个catch,案例演示

try {
    //代码//可能有异常
} catch (NullPointerException e) {
    System.out.println("空指针异常=" + e.getMessage());
} catch (ArithmeticException e) {
    System.out.println("算术异常=" + e.getMessage());
} catch (Exception e) {
    System.out.println(e.getMessage());
}finally {
    
}

5)可以进行try-finally配合使用,这种用法相当于没有捕获异常,因此程序会直接崩掉。(应用场景:执行一段代码,不管是否发生异常,都必须执行某个业务逻辑)

小结

1)如果没有出现异常,则执行try块中所有语句,不执行catch块中语句,如果有finally,最后还需要执行finally里面的语句

2)如果出现异常,则try块中异常发生后,try块剩下的语句不再执行。将执行catch块中的语句,如果有finally,最后还需要执行finally里面的语句!

try-catch实践

package com.gx.test;

import java.util.Scanner;

public class Test {
    public static void main(String[] args) {
        //如果用户输入的不是一个整数,就提示他反复输入,直到输入一个整数为止思路
        // 1,创建Scanner对象
        // 2。使用无限循环,去接收一个输入
        // 3.然后将该输入的值,转成一个int
        // 4.如果在转换时,抛出异常,说明输入的内容不是一个可以转成int的内容
        // 5.如果没有抛出异常,则break该循环
        Scanner scanner = new Scanner(System.in);
        int num = 0;
        String inputStr = "";
        while (true) {
            System.out.println("请输入一个整毁: ");
            inputStr = scanner.next();
            try {
                num = Integer.parseInt(inputStr);//这里是可能抛出异常
                break;
            } catch (NumberFormatException e) {
                System.out.println("你输入的不是一个整数:");
            }
        }
        System.out.println("你输入的值是=" + num);
    }
}
package com.gx.test;

import java.util.Scanner;
//自创方法,不使用try-catch语法,代码更少,使用了Scanner中的hasNextInt()方法,但是其底层源代码仍旧是使用了try-catch语法且原理同上面的相同
public class Test {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        System.out.printf("请输入一个整毁:");
        while (!(scanner.hasNextInt())) {
            System.out.print("您输入的不是整型,请重新输入:");
            scanner.next();
        }
        int num = scanner.nextInt();
        System.out.println("你输入的值是=" + num);
    }
}

throws异常处理

基本介绍
1)如果一个方法(中的语句执行时)可能生成某种异常,但是并不能确定如何处理这种异常,则此方法应显示地声明抛出异常,表明该方法将不对这些异常进行处理,而由该方法的调用者负责处理。

2)在方法声明中用throws语句可以声明抛出异常的列表,throws后面的异常类型可以是方法中产生的异常类型,也可以是它的父类

public void f2() throws FileNotFoundException,NullPointerException , ArithmeticException {
    // 1,这里的异常是一个FileNotFoundException 编译异常
    // 2.使用前面讲过的 try-catch-finally
    // 3.使用throws ,抛出异常,让调用f2方法的调用者(方法)处理
    // 4. throws后面的异常类型可以是方法中产生的异常类型,也可以是它的父类
    // 5. throws关键字后也可以是异常列表,即可以抛出多个异常
    FileInputStream fis = new FileInputStream( "d : //aa.txt");
}

注意事项和细节讨论

1)对于编译异常,程序中必须处理,比如try-catch或者throws

2)对于运行时异常,程序中如果没有处理,默认就是throws的方式处理

3)子类重写父类的方法时,对抛出异常的规定:子类重写的方法,所抛出的异常类型要么和父类抛出的异常一致,要么为父类抛出的异常的类型的子类型

class Father {//父类
    public void method() throws RuntimeException {
    }
}

class Son extends Father {//子类
    // 3,子类重写父类的方法时,对抛出异常的规定:子类重写的方法,
    //所抛出的异常类型要么和父类抛出的异常一致,要么为父类抛出的异常类型的子类型
    @Override
    public void method() throws NullPointerException {
    }
}

4)在throws 过程中,如果有方法 try-catch,就相当于处理异常,就可以不必throws

public static void f1() {
        //这里大家思考问题调用f3()报错
        // 1.因为f3() 方法抛出的是一个编译异常
        // 2.即这时,就要去f1()必须处理这个编译异常
        // 3.在f1()中,要么try-catch-finally,或者继续throws这个编译异常
        f3();// 抛出异常
    }


public static void f3() throws FileNotFoundException {
    FileInputStream fis = new FileInputStream("d: //aa.txt");
}

public static void f4() {
    //老韩解读:
    // 1. 在f4()中调用方法f5()是OK
    // 2.原因是f5()抛出的是运行异常
    // 3. 而java中,并不要求程序员显示处理,因为有默认处理机制f5();
}

public static void f5() throws ArithmeticException {
}

自定义异常

基本介绍

当程序中出现了某些“错误”,但该错误信息并没有在Throwable子类中推述处理,这个时候可以自己设计异常类,用于描述该错误信息。

1)定义类:自定义异常类名(程序员自己写)继承Exception或RuntimeException

2)如果继承Exception,属于编译异常

3)如果继承RuntimeException,属于运行异常(一般来说,继承RuntimeException)

public class Test {
    public static void main(String[] args) {
        int age = 180;
        //要求范围在18 - 120之间,否则抛出一个自定义异常
        if (!(age >= 18 && age <= 120)) {
            //这里我们可以通过构造器,设置信息
            throw new AgeException("年龄需要在18~120之间");
        }
        System.out.println("你的年龄范围正确.");
    }

}
// 1.一般情况下,我们自定义异常是继承 RuntimeException
// 2.即把自定义异常做成运行时异常,好处时,我们可以使用默认的处理机制
// 3.即比较方便
class AgeException extends RuntimeException {
    public AgeException(String message) {//构造器
        super(message);
    }
}

throw与throws的区别

意义位置后面跟的东西
throws异常处理的一种方式方法声明处异常类型
throw手动生成异常对象的关键字方法体中异常对象

常用类

包装类

八种包装类

1.针对八种基本数据类型相应的引用类型—包装类

2.有了类的特点,就可以调用类中的方法。

基本数据类型包装类
booleanBoolean
charCharacter
byteByte
shortShort
intlnteger
longLong
floatFloat
doubleDouble

image-20211106161117669

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

装箱与拆箱

public class Test {
    public static void main(String[] args) {
        //jdk5之前是手动装箱和拆箱
        int n1 = 180;
        Integer integer = new Integer(n1);
        Integer integer1 = Integer.valueOf(n1);
        //手动拆箱
        //Integer -> int
        int i = integer.intValue();

        //jdk5后,就可以自动装箱和自动拆箱
        int n2 = 200;
        // 自动装箱int->Integer
        Integer integer2 = n2;//底层使用的是Integer.valueOf(n2)//自动拆箱Integer->int
        int n3 = integer2;//底层仍然使用的是 intValue()方法
    }
}
public class Test {
    public static void main(String[] args) {
        Object obj1 = true ? new Integer(1) : new Double(2.0);//三元运算符【是一个整体】一真大师
        System.out.println(obj1);//什么?1.0
        
        Object obj2;
        if (true)
            obj2 = new Integer(1);
        else
            obj2 = new Double(2.0);
        System.out.println(obj2);//输出什么?1 分别计算
    }
}
public class Test {
    public static void main(String[] args) {
        //包装类(Integer)->String
        Integer i = 100;//自动装箱
        // 方式1
        String str1 = i + "";
        //方式2
        String str2 = i.toString();
        //方式3
        String str3 = String.valueOf(i);

        // String ->包装类(Integer)
        String str4 = "123456";
        Integer i1 = Integer.parseInt(str4);//自动装箱
        Integer i2 = new Integer(str4);
    }
}
包装类方法

1)构造方法和XXX.valueOf()都可以把基本数据类型变成包装类或者把字符串变成包装类(Chracter除外)

2)XXValue()可以把包装类转换成基本类型

3)DarseXXX(方法(Character除外)可以把字符串变回基本类型

4)toString()方法、String类的valueOf()和+””方法可以把基本类型转换成字符串

public class Test {
    public static void main(String[] args) {
        System.out.println(Integer.MIN_VALUE);//返回最小值
        System.out.println(Integer.MAX_VALUE);//返回最大值

        System.out.println(Character.isDigit('a'));//判断是不是数字
        System.out.println(Character.isLetter('a'));//判断是不是字母
        System.out.println(Character.isUpperCase('a'));//判断是不是大写
        System.out.println(Character.isLowerCase('a'));//判断是不是小写
        
        System.out.println(Character.isWhitespace('a'));//判断是不是空格
        System.out.println(Character.toUpperCase('a'));//转成大写
        System.out.println(Character.toLowerCase('A'));//转成小写
    }
}

源码例题:

public class Test {
    public static void main(String[] args) {
        Integer i = new Integer(1);Integer j = new Integer(1);
        System.out.println(i == j);//False

        //所以,这里主要是看范围-128~127就是直接返回
        Integer m = 1;//底层Integer.valueOf(1);->阅读源码
        Integer n = 1;//底层 Integer.valueOf(1);
        System.out.println(m == n); //True
        Integer.valueOf(1);
        //所以,这里主要是看范围-128~127就是直接返回
        //否则,就new Integer(ox);
        Integer x = 128;//底层
        Integer y = 128;
        System.out.println(x == y);//False

        /*Integer.valueOf(1)源码:
        //1.如果i 在 IntegerCache.low(-128)~IntegerCache.high(127),就直接从数组返回
        //2.如果不在-128~127,就直接,new
            public static Integer valueOf(int i) {
                if (i >= Integer.IntegerCache.low && i <= Integer.IntegerCache.high)//low=-128 high=127
                    return Integer.IntegerCache.cache[i + (-Integer.IntegerCache.low)];
                return new Integer(i);
            }
        */
    }
}
public class Test {
    public static void main(String[] args) {
        //有new肯定就是新的对象
        
        //示例一
        Integer i1 =new Integer( 127);
        Integer i2=new Integer( 127);
        System.out.println(i1==i2);//F

        // 示例二
        Integer i3=new Integer( 128 );
        Integer i4=new Integer( 128 );
        System.out.println(i3==i4);//F

        // 示例三
        Integer i5=127;//底层Integer.valueOf(127)
        Integer i6=127;//-128~127
        System.out.println(i5==i6); //T

        // 示例四
        Integer i7=128;
        Integer i8=128;
        System.out.println(i7==i8);//F

        // 示例五
        Integer i9=127; //Integer.valueOf(127)
        Integer i10=new Integer(127);
        System.out.println(i9==i10);//F

    }
}
public class Test {
    public static void main(String[] args) {
        //只有有基本数据类型,判断的是值是否相同
        //这是因为跟基本数据类型比较时,会自动拆箱

        //示例六
        Integer i11=127;
        int i12=127;
        System.out.println(i11==i12);

        //示例七
        Integer i13=128;
        int i14=128;
        System.out.println(i13==i14);
    }
}

String类

String类的理解与创建对象

1)String对象用于保存字符串,也就是一组字符序列

2)字符串常量对象是用双引号括起的字符序列。例如:“你好”、“12.97”、"boy"等

3)字符串的字符使用Unicode字符编码,一个字符(不区分字母还是汉字)占两个字节。

4)String类较常用构造器(其它看手册):

String s1 = new String();
String s2 = new String(String original);
String s3 = new String(char[]a);
String s4 = new String(char[]a,int startIndex,int count);
String s5 = new String(byte[] b)//网络编程用得多

5)String类实现了接口 Serializable [String 可以串行化:可以在网络传输]
接口Comparable [String 对象可以比较大小]

image-20211108152501118

6)String是final类,不能被其他的类继承

7)String有属性 private final char value[];用于存放字符串内容

8)一定要注意: value是一个final类型,不可以修改(需要功力):即value不能指向新的地址,但是单个字符内容是可以变化

final char[] value = { 'a', 'b','c'};
char[] v2 = { 't','o', 'm'};
value[0]= 'H';
//value = v2;不可以修改value地址
创建String对象的两种方式
String s = "hsp";//方式一:直接赋值
String s2 = new String("hsp");//方式二:调用构造器 

1.方式一:先从常量池查看是否有"hsp”数据空间,如果有,直接指向;如果没有则重新创建,然后指向。S最终指向的是常量池的空间地址

2.方式二:先在堆中创建空间,里面维护了value属性,指向常量池的hsp空间。如果常量池没有"hsp",重新创建,如果有,直接通过value指向。最终指向的是堆中的空间地址。

3.两种方式的内存分布图

image-20211108155505294

public class Test {
    public static void main(String[] args) {
        String a = "hsp"; //a指向常量池的“hsp"
        String b = new String("hsp");//b指向堆中对象
        System.out.println(a.equals(b));//T
        System.out.println(a == b);//F
        //b.intern()方法返回常量池地址
        System.out.println(a == b.intern()); //T
        System.out.println(b == b.intern());//F
//        知识点:
//        当调用intern方法时,如果池已经包含一个等于此 String对象的字符串(用equals(Object)方法确定),则返回池中的字符串。否则,将此String 对象添加到池中,并返回此 String对象的引用
//        老韩解读:(1) b.intern()方法最终返回的是常量池的地址(对象)﹒
    }
}

上课的例子,原理介绍图仅作参考

		String str1 = "bdqn";
        String str2 = "bdqn" ;
        System.out.println(str1==str2);//ture
        System.out.println(str1.equals(str2));//ture
        String str3 = new String( "bdqn");
        System.out.println(str1==str3);//false
        System.out.println(str1.equals(str3));//ture
        String str4 = new String( "bdqn");
        System.out.println(str3==str4);//false
        System.out.println(str3.equals(str4));//ture

我们都知道,在运算符中“==”的结果比较的是地址,地址相同时为ture,不同为false

而字符串的equals()方法比较的是字符串的内容是否相同,相同时为ture,不同为false

然而在此例子当中当没有给String变量开辟空间时,如果其复制为常量,其地址时相同的(即st1与st2)

而当我们给String变量开辟空间后,哪怕其复制内容相同,但是地址也不同了。

其原理就是常量的地址时确定的,“=”只是进行一个指针的指向操作,并没有给其分配空间

其原理如下:

image-20211025172325484

String的特性

1)String是一个final类,代表不可变的字符序列

2)字符串是不可变的。一个字符串对象一旦被分配,其内容是不可变的.

String a = "hello"+"abc";
创建了几个对象?只有1个对象.
//String a = "hello"+"abc"; //==>优化等价 String a = "helloabc";
//分析:
//1.编译器不傻,做一个优化,判断创建的常量池对象,是否有引用指向
//2.String a = "hello"+"abc"; =》String a = "helloabc";
public class Test {
    public static void main(String[] args) {
        String a = "hello";//创建a对象
        String b = "abc ";//创建 b对象
        //老韩解读
        // 1.先创建一个StringBuilder sb = StringBuilder()
        // 2.执行sb.append("hello");
        // 3. sb.append("abc " );
        // 4. String c= sb.toString()
        // 最后其实是c指向堆中的对象(String) value[] -> 池中"helloabc"
        String c = a + b;
        String d = "helloabc";
        System.out.println(c == d);//false
        String e = "hello" + "abc";//直接看池,e指向常量池
        System.out.println(e == d);//true
    }
}

image-20211108162637849

老韩小结:底层是StringBuilder sb = new StringBuilder(); sb.append(a);sb.append(b); sb是在堆中,并且append是在原来字符串的基础上追加的.

重要规则:

String c1 = “ab” + “cd”;常量相加,看的是池。

String c1 = a+b;变量相加,是在堆中

特性题

public class Test {
    String str = new String("hsp");
    final char[] ch = {'j', 'a', 'v', 'a'};

    public void change(String str, char ch[]) {
        str = "java";
        ch[0] = 'h';
    }

    public static void main(String[] args) {
        Test ex = new Test();
        ex.change(ex.str, ex.ch);
        System.out.print(ex.str + " and ");
        System.out.println(ex.ch);
    }
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-S6RdfhTt-1636722245126)(http://www.gdx0326.top:20014/static/java笔记.assets/image-20211108164547521.png)]

String的常用方法
public int indexOf(int ch)搜索第一个出现的字符ch,如果没有找到,返回-1
public int indexOf(String value)搜索第一个出现的字符串value,如果没有找到,返回-1
public int lastIndexOf(int ch)搜索最后一个出现的字符ch,如果没有找到,返回-1
public int lastIndexOf(String value)搜索最后一个出现的字符串value,如果没有找到,返回-1
public String substring(int index)提取从位置索引开始的字符串部分
public String substring(int beginindex,int endindex)提取beginindex和endindex之间的字符串部分,[beginindex, endindex-1]
public String trim()返回一个前后不含任何空格的调用字符串的副本
equalslgnoreCase()忽略大小写的判断内容是否相等
charAt(int i)获取i位置索引处的字符
public class Test {
    public static void main(String[] args) {
        // 1.toUpperCase转换成大写
        String s = "heLLo";
        System.out.println(s.toUpperCase());//HELLOl
        // 2.toLowerCase
        System.out.println(s.toLowerCase());//hello
        // 3.concat拼接字符串
        String s1 = "宝玉";
        s1 = s1.concat("林黛玉").concat("薛宝钗").concat("together");
        System.out.println(s1);//宝玉 林黛玉 薛宝钗 together
        // 4.replace替换字符串中的字符
        s1 = "宝玉 and 林黛玉林黛玉林黛玉";
        //在s1中,将所有的林黛玉替换成薛宝钗
        //解读:s1.replace()方法执行后,返回的结果才是替换过的
        // 注意对s1没有任何影响,这里是将返回结果重新赋值了
        s1 = s1.replace("林黛玉", "薛宝钗");
        System.out.println(s1);//宝玉 and 薛宝钗 薛宝钗 薛宝钗
        // 5.split分割字符串,对于某些分割字符,我们需要转义比如│\\等
        String poem = "锄禾日当午,汗滴禾下土,谁知盘中餐,粒粒皆辛苦";
        // 1.以","为标准对poem进行分割,、返回一个数组
        // 2.在对字符串进行分割时,如果有特殊字符,需要加入转义符/
        String[] split = poem.split(",");
//        poem = "E:\\aaa\\bbb";
//        split = poem.split("\\\\");
        for (int i = 0; i < split.length; i++) {
            System.out.println(split[i]);
        }
        // 6.toCharArray转换成字符数组
        s = "happy";
        char[] chs = s.toCharArray();
        for (int i = 0; i < chs.length; i++) {
            System.out.println(chs[i]);
        }
        //7.compareTo 比较两个字符串的大小,如果前者大,则返回正数,后者大,则返回负数,如果相等,返回0
        // (1)如果长度相同,并且每个字符也相同,就返回0
        // (2)如果长度相同或者不相同,但是在进行比较时,可以区分大小
        //  就返回if (c1 != c2) {
        //                return c1 - c2;
        //            }
        // (3)如果前面的部分都相同,就返回str1.len - str2.len
        String a = "jcck";
        String b = "jack";
        System.out.println(a.compareTo(b));//返回值是 'c' - 'a' = 2的值
        // 8.format格式字符串
        /*占位符有:
         * %S 字符串 %c 字符 %d 整型 %.2f 浮点型
         *
         */
        String name = "john";
        int age = 10;
        double score = 98.3 / 3;
        char gender = '男';
        String info = "我的姓名是:" + name + ",性别是" + age + ",成绩是" + score + "性别是" + gender + "。希望大家喜欢";
        System.out.println(info);
        // 1.%s , %d , %.2f %c称为占位符
        // 2.这些占位符由后面变量来替换
        // 3.%s 表示后面由字符串来替换
        // 4.%d是整数来替换
        // 5.%.2f 表示使用小数来替换,替换后,只会保留小数点两位,并且进行四舍五入的处理
        // 6.%c使用char类型来替换
        String formatStr = "我的姓名是%s 性别是%d 成绩是%.2f 性别是%c 希望大家喜欢";
        String info2 = String.format(formatStr, name, age, score, gender);
        System.out.println("info2" + info2);
    }
}

String类是保存字符串常量的。每次更新都需要重新开辟空间,效率较低,因此java设计者还提供了StringBuilder和StringBufer来增强String的功能,并提高效率。

String翻转题
public class Test {
    public static void main(String[] args) {
        //测试
        String str = "abcdef";
        System.out.println("===交换前===");
        try {
            System.out.println(str);
        } catch (Exception e) {
            System.out.println(e.getMessage());
            return;
        }
        str = reverse(str, 1, 58);
        System.out.println("===交换后===");
        System.out.println(str);

    }

    public static String reverse(String str, int start, int end) {
        if(!(str != null && start >= 0 && end > start && end < str.length())) {
            throw new RuntimeException("参数不正确");
        }
        char[] chars = str.toCharArray();
        char temp = ' ';
        for (int i = start, j = end; i < j; i++, j--) {
            temp = chars[i];
            chars[i] = chars[j];
            chars[j] = temp;
        }
        return new String(chars);
    }
}

StringBufer类

基本介绍

1)java.lang.StringBuffer代表可变的字符序列,可以对字符串内容进行增删。

2)很多方法与String相同,但StringBuffer是可变长度的。

3)StringBuffer是一个容器。

public class Test {
    public static void main(String[] args) {
        // 1.StringBuffer的直接父类是 AbstractStringBuilder
        // 2.StringBuffer实现了Serializable,即StringBuffer的对象可以串行化(序列化)
        // 3.在父类中 AbstractStringBuilder有属性 char[] value,不是final 该value 数组存放字符串内容,引出存放在堆中的
        // 4. StringBuffer是一个 final类,不能被继承
        // 5.因为StringBuffer字符内容是存在 char[] value,所以在变化(增加/删除)时不用每次都更换地址(即不是每次创建新对象),所以效率高于String
        StringBuffer stringBuffer = new StringBuffer();
    }
}
String Vs StringBuffer

1)String保存的是字符串常量,里面的值不能更改,每次String类的更新实际上就是更改地址,效率较低//private final char value[];

2)StringBuffer保存的是字符串变量,里面的值可以更改,每次StringBuffer的更新实际上可以更新内容,不用更新地址,效率较高//char[] value;//这个放在堆.

StringBuffer构造器
public class Test {
    public static void main(String[] args) {

        //1.创建一个大小为16的 char[],用于存放字符内
        /*源码
            @HotSpotIntrinsicCandidate
            public StringBuffer() {
                super(16);
            }
        */
        StringBuffer stringBuffer = new StringBuffer();

        //2通过构造器指定char[]大小
        /*源码
            @HotSpotIntrinsicCandidate
            public StringBuffer(int capacity) {
                super(capacity);
            }
        */
        StringBuffer stringBuffer1 = new StringBuffer(100);

        //3.通过给一个String创建StringBuffer
        /*源码
            @HotSpotIntrinsicCandidate
            public StringBuffer(String str) {
                super(str);
            }

            AbstractStringBuilder(String str) {
                int length = str.length();
                int capacity = (length < Integer.MAX_VALUE - 16)
                        ? length + 16 : Integer.MAX_VALUE;
                final byte initCoder = str.coder();
                coder = initCoder;
                value = (initCoder == LATIN1)
                        ? new byte[capacity] : StringUTF16.newBytesFor(capacity);
                append(str);//将"hello"添加进去
            }
        */
        StringBuffer helld = new StringBuffer("hello");
    }
}
StringBuffer转换
public class Test {
    public static void main(String[] args) {
        //看String-->StringBuffer
        String str = "hello tom";
        //方式1 使用构造器
        // 注意:返回的才是StringBuffer对象,对str 本身没有影响
        StringBuffer stringBuffer = new StringBuffer(str);
        //方式2 使用的是append方法
        StringBuffer stringBuffer1 = new StringBuffer();
        stringBuffer1 = stringBuffer1.append(str);

        //看看 StringBuffer-->String
        StringBuffer stringBuffer3 = new StringBuffer();
        //方式1使用StringBuffer提供的toString方法
        String s = stringBuffer3.toString();
        //方式2:使用构造器来搞定
        String s1 = new String(stringBuffer3);

    }
}
StringBuffer方法

1)增append

2)delete(start,end)

3)改replace(start,end,string)//将start----end间的内容替换掉,不含end

4)查indexOf//查找子串在字符串第1次出现的索引,如果找不到返回-1

5)插insert

6)获取长度length

public class Test {
    public static void main(String[] args) {
        StringBuffer s = new StringBuffer("hello");
        //增
        s.append(',');// "hello, "
        s.append("张三丰");//"hello,张三丰"
        s.append("赵敏").append(100).append(true).append(10.5);
        ;//"hello,张三丰赵敏100true10.5
        System.out.println(s);
        //删
        /*
            删除索引为>=start &&<end 处的字符
            解读:删除11~14的字符
        */
        s.delete(11, 14);
        System.out.println(s);
        //改
        //使用周芷若替换索引9-11的字符[9,11]
        s.replace(9, 11, "周芷若");
        System.out.println(s);//"hello,张三丰周芷若true10.5"
        //查找指定的子中在字符串第一次出现的索引,如果找不到返回-1
        int indexOf = s.indexOf("张三丰");
        System.out.println(indexOf);//6
        //插
        // hello,张三丰周芷若true10.5
        //在索引为9的位置插入"赵敏",原来索引为9的内容自动后移
        s.insert(9, "赵敏");// hello,张三丰赵敏周芷若true10.5
        System.out.println(s);
        //长度
        System.out.println(s.length());//22
        System.out.println(s);
    }
}

StringBuilder

基本介绍

1)一个可变的字符序列。此类提供一个与StringBuffer兼容的API,但不保证同步(StringBuilder 不是线程安全)。该类被设计用作 StringBuffer的一个简易替换,用在字符串缓冲区被单个线程使用的时候。如果可能,建议优先采用该类,因为在大多数实现中,它比StringBuffer 要快。

2)在 StringBuilder上的主要操作是append和insert方法,可重载这些方法,以接受任意类型的数据。

StringBuilder的常用方法

StringBuilder和 StringBuffer均代表可变的字符序列,方法是一样的,所以使用和StringBuffer一样

public class Test {
    public static void main(String[] args) {
        // 1.StringBuilder继承AbstractStringBuilder 类
        // 2.实现了Serializable ,说明StringBuilder对象是可以串行化(对象可以网络传输,可以保存到文件)
        // 3.StringBuilder 是final类,不能被继承
        // 4.StringBuilder|对象字符序列仍然是存放在其父类AbstractStringBuilder的 char[] value;// 因此,字符序列是堆中
        // 5.StringBuilder的方法,没有做互斥的处理,即没有synchronized 关键字,因此在单线程的情况下使用StringBuilder
        StringBuilder stringBuilder = new StringBuilder();
    }
}

String、StringBuffer 和StringBuilder的比较

1)StringBuilder 和 StringBuffer非常类似,均代表可变的字符序列,而且方法也一样

2)String:不可变字符序列,效率低,但是复用率高。

3)StringBuffer:可变字符序列、效率较高(增删)、线程安全

4)StringBuilder:可变字符序列、效率最高、线程不安全

5)String使用注意说明:

string s=“a”;1/创建了一个字符串

s += “b”;//实际上原来的"a"字符串对象已经丢弃了,现在又产生了一个字符串s+“b”(也就是"ab”)。如果多次执行这些改变串内容的操作,会导致大量副本字符串对象存留在内存中,降低效率。如果这样的操作放到循环中,会极大影响程序的性能=>结论:如果我们对String 做大量修改,不要使用String

效率比较测试
public class Test {
    public static void main(String[] args) {
        long startTime = 0L;
        long endTime = 0L;
        StringBuffer buffer = new StringBuffer("");
        startTime = System.currentTimeMillis();
        for (int i = 0; i < 20000; i++) {//StringBuffer拼接20000次
            buffer.append(String.valueOf(i));
        }
        endTime = System.currentTimeMillis();
        System.out.println("StringBuffer的执行时间:" + (endTime - startTime));//约4

        StringBuilder builder = new StringBuilder("");
        startTime = System.currentTimeMillis();
        for (int i = 0; i < 20000; i++) {//StringBuilder拼接20000次
            builder.append(String.valueOf(i));
        }
        endTime = System.currentTimeMillis();
        System.out.println("StringBuilder的执行时间:" + (endTime - startTime));//约3


        String text = "";
        startTime = System.currentTimeMillis();
        for (int i = 0; i < 20000; i++) {//String拼接20000
            text = text + i;
        }
        endTime = System.currentTimeMillis();
        System.out.println("String的执行时间:" + (endTime - startTime));//约226

    }
}

效率:StringBuilder > StringBuffer > String

选择方式的总结

使用的原则,结论:

1)如果字符串存在大量的修改操作,一般使用 StringBuffer 或StringBuilder

2)如果字符串存在大量的修改操作,并在单线程的情况,使用StringBuilder

3)如果字符串存在大量的修改操作,并在多线程的情况,使用 StringBuffer

4)如果我们字符串很少修改,被多个对象引用,使用String,比如配置信息等

StringBuilder的方法使用和 StringBuffer一样

Math类

基本介绍

Math类包含用于执行基本数学运算的方法,如初等指数、对数、平方根和三角函数。

1)abs绝对值

2)pow求幂

3)ceil向上取整

4)floor向下取整

5)round四舍五入

6)sqrt求开方

7)random求随机数//思考:
请写出获取a-b之间的一个随机整数,a,b均为整数?2-7

8)max求两个数的最大值

9)min求两个数的最小值

public class Test {
    public static void main(String[] args) {
        //看看Math常用的方法(静态方法)
        // 1.abs 绝对值
        int abs = Math.abs(8);
        System.out.println(abs);//8
        //2.pow求幂
        double pow = Math.pow(2, 4);
        System.out.println(pow);//16
        //3.ceil向上取整,返回>=该参数的最小整数(转成double)
        double ceil = Math.ceil(-3.0001);
        System.out.println(ceil);//-3
        //4.floor向下取整,返回<=该参数的最大整数(转成double)
        double floor = Math.floor(-4.999);
        System.out.println(floor);//-5
        //5.round 四舍五入 Math.floor(该参数+0.5)
        long round = Math.round(-5.001);
        System.out.println(round);
        //6.sqrt求开方
        double sqrt = Math.sqrt(9.0);
        System.out.println(sqrt);//3.0
        //7.random求随机数
        //random 返回的是 0<= x <1之间的一个随机小数
        //思考:请写出获取a-b之间的一个随机整数,a,b均为整数?比如a=2,b=7
        // 即返回一个数x 2 <= x < 7
        //Math.random() * (b-a)返回的就是 0<= 数 <= b-a
        //(int)(a)<= x <= (int)(a + Math.random() * (b-a +1))
        //(2)使用具体的数给小伙伴介绍 a = 2 b = 7
        //(int)(a + Math . random() * (b-a +1) ) = (int)( 2 + Math. random()*6)
        // Math.random()*6 返回的是 0 <= x < 6小数
        // 2 + Math.random()*6 返回的就是 2 <= x < 8 小数
        //(int)(2 + Math.random()*6) = 2 <= x <= 7
        // 3.公式就是(int)(a + Math.random() * (b-a +1))
        for (int i = 0; i < 10; i++) {
            int sum = (int) (2 + Math.random() * (7 - 2 + 1));
            System.out.println(sum);
        }
        //max , min返回最大值和最小值
        int min = Math.min(2, 9);
        int max = Math.max(45, 90);
        System.out.println("min=" + min);
        System.out.println("max=" + max);
    }
}

Arrays类

Arrays的常用方法

Arrays里面包含了一系列静态方法,用于管理或操作数组(比如排序和搜索)。

1)toString返回数组的字符串形式y

2)sort 排序(自然排序和定制排序)

定制排序源码分析

image-20211111191906140

import java.util.Arrays;
import java.util.Comparator;

public class Test {
    public static void main(String[] args) {
        Integer[] integer = {1, 2, 3};

        //直接使用Arrays.toString方法,显示数组
        System.out.println(Arrays.toString(integer));

        //演示sort方法的使用
        Integer arr[] = {1, -1, 7, 0, 89};
        //1.可以直接使用冒泡排序,也可以直接使用Arrays提供的sort方法排序
        //2.因为数组是引用类型,所以通过sort排序后,会直接影响到实参 arr
        //Arrays.sort(arr);//默认排序方法
        //3. sort重载的,也可以通过传入一个接口 Comparator 实现定制排序
        //4.调用定制排序时,传入两个参数(1)排序的数组arr (2)实现了Comparator接口的匿名内部类,要求实现compare方法
        //6.这里体现了接口编程的方式
        //源码分析
        //(1) Arrays.sort(arr, new Comparator<Integer>(){});
        //(2) 最终到了private static <T> void binarySort(T[] a, int lo, int hi, int start,
        //                                       Comparator<? super T> c) {
        //(3)执行到 binarySort方法的代码,会根据动态绑定机制c.compare()执行我们传入的匿名内部类的 compare方法
        //while (left < right) {
        //                int mid = (left + right) >>> 1;
        //                if (c.compare(pivot, a[mid]) < 0)
        //                    right = mid;
        //                else
        //                    left = mid + 1;
        //            }
        //(4)new Comparator<Integer>() {
        //            @Override
        //            public int compare(Integer o1, Integer o2) {
        //                return o2-o1;
        //            }
        //        }
        //(5) public int compare(Object o1,0bject o2)返回的值>0还是<0会影响整个排序结果
        //这就充分体现了接口编程+动态绑定+匿名内部类的综合使用
        //将来的底层框架和源码的使用方式,会非常常见
        Arrays.sort(arr, new Comparator<Integer>() {
            @Override
            public int compare(Integer o1, Integer o2) {
                return o2 - o1;
            }
        });
        System.out.println("排序后:" + Arrays.toString(arr));
    }
}
模拟定制排序
import java.util.Arrays;
import java.util.Comparator;

public class Test {
    public static void main(String[] args) {
        int[] arr = {1, -1, 8, 0, 20};
        //bubble01(arr);
        bubble02(arr, new Comparator<Integer>() {
            @Override
            public int compare(Integer o1, Integer o2) {
                return o1 - o2;//return o2-o1;
            }
        });
        System.out.println("==排序后的情况==");
        System.out.println(Arrays.toString(arr));
    }


    //使用冒泡完成排序
    public static void bubble01(int[] arr) {
        int temp = 0;
        for (int i = 0; i < arr.length - 1; i++) {
            for (int j = 0; j < arr.length - 1 - i; j++) {
                //从小到大
                if (arr[j] > arr[j + 1]) {
                    temp = arr[j];
                    arr[j] = arr[j + 1];
                    arr[j + 1] = temp;
                }
            }
        }
    }

    //结合冒泡+定制
    public static void bubble02(int[] arr, Comparator<Integer> c) {
        int temp = 0;
        for (int i = 0; i < arr.length - 1; i++) {
            for (int j = 0; j < arr.length - 1 - i; j++) {
                //从小到大
                //数组排序由 c.compare(arr[j],arr[j +1])返回的值决定
                if (c.compare(arr[j], arr[j + 1]) > 0) {
                    temp = arr[j];
                    arr[j] = arr[j + 1];
                    arr[j + 1] = temp;
                }
            }
        }
    }

}
其他常见方法

3)binarySearch 通过二分搜索法进行查找,要求必须排好序

4)copyOf 数组元素的复制

5)fill 数组元素的填充

6)equals 比较两个数组元素内容是否完全一致

7)asList将一组值,转换成list

import java.util.Arrays;
import java.util.List;

public class Test {
    public static void main(String[] args) {
        Integer arr[] = {1, 2, 9, 123, 567};
        // binarySearch 通过二分搜索法进行查找,要求必须排好
        // 1.使用binarySearch二叉查找
        // 2.要求该数组是有序的。如果该数组是无序的,不能使用binarySearch
        // 3.如果数组中不存在该元素,就返回return -(low + 1);
        int index = Arrays.binarySearch(arr, 9);
        System.out.println("index=" + index);

        // copy0f数组元素的复制
        // 1.从 arr数组中,拷贝 arr.length 个元素到newArr数组中
        // 2.如果铂贝的长度 >arr.length 就在新数组的后面增加 null
        // 3.如果拷贝长度 <0 就抛出异常NegativeArraySizeException
        // 4.该方法的底层使用的是System.arraycopy();
        Integer[] newArr = Arrays.copyOf(arr, arr.length);
        System.out.println("==拷贝执行完毕后==");
        System.out.println(Arrays.toString(newArr));

        //fill 数组元素的填充
        //1.使用99去填充num数组,可以理解成是替换原理的元素
        Integer[] num = new Integer[]{9, 3, 2};
        System.out.println("==num数组填充后==");
        Arrays.fill(num, 99);
        System.out.println(Arrays.toString(num));

        //equals比较两个数组元素内容是否完全一致
        Integer[] arr2 = {1, 2, 90, 123};
        //1.如果arr 和 arr2数组的元素一样,则方法true;
        //2.如果不是完全一样,就返回false
        boolean equals = Arrays.equals(arr, arr2);
        System.out.println("equals=" + equals);

        //asList将一组值,转换成list
        // 1.asList方法,会将〔2,3,4,5,6,1)数据转成一个List集
        // 2.返回的 asList 编译类型 List(接口)
        // 3.asList 运行类型 java.util.Arrays$ArrayList,即是Arrays类的静态内部类
        //private static class ArrayList<E> extends AbstractList<E>
        //        implements RandomAccess, java.io.Serializable
        List asList = Arrays.asList(2, 3, 4, 5, 6, 1);
        System.out.println("asList=" + asList);
        System.out.println("asList的运行类型" + asList.getClass());

    }
}

System类

常见方法

1)exit 退出当前程序

2)arraycopy :复制数组元素,比较适合底层调用,一般使用Arrays.copyOf完成复制数组.

3)currentTimeMillens:返回当前时间距离1970-1-1的毫秒数

4)gc:运行垃圾回收机制System.gc();

import java.util.Arrays;

public class Test {
    public static void main(String[] args) {
        //exit退出当前程序
        System.out.println("ok1");
        // 1.exit(0)表示程序退出
        // 2.0表示一个状态,正常的状态
//        System.exit(0);
        System.out.println("ok2");

        //arraycopy :复制数组元素,比较适合底层调用,一般使用Arrays.copyOf完成复制数组.
        int[] src = {1, 2, 3};
        int[] dest = new int[3];//dest当前是{0,0,0}
        //1主要是搞清楚这五个参数的含义
        //2.
        // Params:
        // src:原数组 (src – the source array.)
        // srcPos:从原数组哪位置开始拷贝 (srcPos – starting position in the source array.)
        // dest:目标数组,即把源数组的数据拷贝到哪个数组(dest – the destination array.)
        // destPos:把源数组的数据拷贝到目标数组的哪个索引(destPos – starting position in the destination data.)
        // length:从源数组拷贝多少个数据到目标数组(length – the number of array elements to be copied.)
        System.arraycopy(src, 0, dest, 0, src.length);
        //int[] src={1,2,3};
        System.out.println("dest" + Arrays.toString(dest));//123

        //currentTimeMillens:返回当前时间距离1970-1-1的毫秒数
        System.out.println(System.currentTimeMillis());
    }
}

Biglnteger类和BigDecimal类

基本介绍

应用场景:
1)Biglnteger适合保存比较大的整型

public class Test {
    public static void main(String[] args) {

        //当我们编程中,需要处理很大的整数,long不够用
        //可以使用BigInteger的类来搞定
//        long l = 237887989797979998785788l;
//        System.out.println("l=" + l);
        BigInteger bigInteger = new BigInteger("237887989797979998785788");
        BigInteger bigInteger1 = new BigInteger("1002124244");
        System.out.println(bigInteger);
        // 1.在对BigInteger进行加减乘除的时候,需要使用对应的方法,不能直接进行 + – * /
        // 2.可以创建一个要操作的BigInteger然后进行相应操作
        BigInteger add = bigInteger.add(bigInteger1);
        System.out.println(add);//加
        BigInteger subtract = bigInteger.subtract(bigInteger1);
        System.out.println(subtract);//减
        BigInteger multiply = bigInteger.multiply(bigInteger1);
        System.out.println(multiply);//乘
        BigInteger divide = bigInteger.divide(bigInteger1);
        System.out.println(divide);//除
    }
}

2)BigDecimal适合保存精度更高的浮点型(小数)

import java.math.BigDecimal;

public class Test {
    public static void main(String[] args) {
        //当我们需要保存一个精度很高的数时,double 不够用
        //可以是 BigDecimal
//        double d = 1999.11111111111999999999999977788d;
//        System.out.println(d);
        BigDecimal bigDecimal = new BigDecimal("1999.11111111111999999999999977788");
        BigDecimal bigDecimal1 = new BigDecimal("1.1");
        System.out.println(bigDecimal);
        // 1.如果对 BigDecimal进行运算,比如加减乘除,需要使用对应的方法
        // 2.创建一个需要操作的 BigDecimal然后调用相应的方法即可

        System.out.println(bigDecimal.add(bigDecimal1));
        System.out.println(bigDecimal.subtract(bigDecimal1));
        System.out.println(bigDecimal.multiply(bigDecimal1));
        //System.out.println(bigDecimal.divide(bigDecimal1));//可能抛出异常ArithmeticException
        //在调用divide 方法时,指定精度即可,BigDecimal.ROUND_CEILIN
        //如果有无限循环小数,就会保留分子的精度
        System.out.println(bigDecimal.divide(bigDecimal1,BigDecimal.ROUND_CEILING));
    }
}

日期类

第一代日期类Date

1)Date:精确到毫秒,代表特定的瞬间

2)SimpleDateFormat:格式和解析日期的类SimpleDateFormat格式化和解析日期的具体类。它允许进行格式化(日期->文本)、解析(文本->日期)和规范化.

image-20211111212725613

public class Test {
    public static void main(String[] args) {

        // 1.获取当前系统时间
        // 2.这里的Date类是在java.util包
        // 3.默认输出的日期格式是国外的方式,因此通常需要对格式进行转换
        Date d1 = new Date();
        System.out.println("当前日期=" + d1);

        Date d2 = new Date(9234567);//通过指定毫秒数得到时间
        System.out.println("d2" + d2);
        System.out.println(d1.getTime());//获取某个时间对应的毫秒数

        // 1。创建SimpleDateFormat对象,可以指定相应的格式
        // 2.这里的格式使用的字母是规定好,不能乱写
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日hh:mm:ss E");
        String format = sdf.format(d1); // format:将日期转换成指定格式的字符串
        System.out.println(format);
        //1。可以把一个格式化的String转成对应的Date
        //2.得到Date仍然在输出时,还是按照国外的形式,如果希望指定格式输出,需要转换
        //3.在把String -> Date ,使用的 sdf 格式需要和你给的String的格式一样,否则会抛出转换异
        String s = "1996年01月01日 10:20:30 星期一";
        try {
            Date parse = sdf.parse(s);
            System.out.println(parse);
        } catch (ParseException e) {
            e.printStackTrace();
        }

    }
}
第二代日期类Calendar

1)第二代日期类,主要就是Calendar类(日历)。

public abstract class Calendar implements Serializable, Cloneable, Comparable<Calendar>{}

2)Calendar类是一个抽象类,它为特定瞬间与一组诸如YEAR、MONTH、DAY_OF MONTH、HOUR等日历字段之间的转换提供了一些方法,并为操作日历字段(例如获得下星期的日期)提供了一些方法。

import java.util.Calendar;

public class Test {
    public static void main(String[] args) {
        // 1.Calendar是一个抽象类,并且构造器是private
        // 2.可以通过getInstance()来获取实例
        // 3.提供大量的方法和字段提供给程序员
        // 4.Calendar没有提供对应的格式化的类,因此需要程序员自己组合来输出(灵活)
        //5,如果我们需要按照24小时进制来获取时间,Calendar.HOUR ==改成=> Calendar.HOUR_OF_DAY
        // Calendar
        Calendar c = Calendar.getInstance();//创建日历类对象//比较简单,自由
        System.out.println(c);

        //获取日历对象的某个日历字段
        System.out.println("年:" + c.get(Calendar.YEAR));
        //这里为什么要+1,因为Calendar返回月时候,是按照0开始编号
        System.out.println("月:" + (c.get(Calendar.MONTH) + 1));
        System.out.println("日:" + c.get(Calendar.DAY_OF_MONTH));
        System.out.println("小时:" + c.get(Calendar.HOUR));
        System.out.println("分钟:" + c.get(Calendar.MINUTE));
        System.out.println("秒:" + c.get(Calendar.SECOND));
        //Calender 没有专门的格式化方法,所以需要程序员自己来组合显示
        System.out.println(c.get(Calendar.YEAR) + "-" + (c.get(Calendar.MONTH) + 1) + "-" + c.get(Calendar.DAY_OF_MONTH)
                + " " + c.get(Calendar.HOUR_OF_DAY) + ":" + c.get(Calendar.MINUTE) + ":" + c.get(Calendar.SECOND));
    }
}
第三代日期类LocalDateTime

前面两代日期类的不足分析
JDK 1.0中包含了一个java.util.Date类,但是它的大多数方法已经在JDK 1.1引入Calendar类之后被弃用了。

而Calendar也存在问题是:
1)可变性:像日期和时间这样的类应该是不可变的。

2)偏移性:Date中的年份是从1900开始的,而月份都从0开始。

3)格式化:格式化只对Date有用,Calendar则不行。

4)此外,它们也不是线程安全的;

5)不能处理闰秒等(每隔2天,多出1s).

LocalDateTime常见方法

1)LocalDate(日期/年月日)、LocalTime(时间/时分秒)、LocalDateTime(日期时间/年月日时分秒) JDK8加入

LocalDate只包含日期,可以获取日期字段

localTime只包含时间,可以获取时间字段

LocalDateTime包含日期+时间,可以获取日期和时间字段

2)DateTimeFormatter格式日期类类似于SimpleDateFormat

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;

public class Test {
    public static void main(String[] args) {
        //第三代日期
        //1.使用now()返回表示当前日期时间的对象
        LocalDateTime ldt = LocalDateTime.now();//LocalDate.now();//LocalTime.now()
        System.out.println(ldt);
        System.out.println("年=" + ldt.getYear());
        System.out.println("月=" + ldt.getMonthValue());
        System.out.println("日=" + ldt.getDayOfMonth());
        System.out.println("时=" + ldt.getHour());
        System.out.println("分=" + ldt.getMinute());
        System.out.println("秒=" + ldt.getSecond());
        //2.使用DateTimeFormatter对象来进行格式化//创建DateTimeFormatter对象
        DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy年MM月dd日 HH小时mm分钟ss秒");
        String format = dateTimeFormatter.format(ldt);
        System.out.println(format);
    }
}

3)Instant时间戳类似于Date

提供了一系列和Date类转换的方式

import java.time.Instant;
import java.util.Date;

public class Test {
    public static void main(String[] args) {
        //1.通过静态方法now()获取表示当前时间戳的对象
        Instant now = Instant.now();
        System.out.println(now);
        //2.通过 from可以把Instant转成Date
        Date date = Date.from(now);
        //3.通过date的toInstant()可以把 date 转成Instant对象
        Instant instant = date.toInstant();
    }
}

4)第三代日期类更多方法·

LocalDateTime类

MonthDay类:检查重复事件

是否是闰年

增加日期的某个部分

使用plus方法测试增加时间的某个部分

使用minus方法测试查看一年前和一年后的日期

其他的方法,使用的时候,自己查看API使用即可

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;

public class Test {
    public static void main(String[] args) {
        //第三代日期
        //1.使用now()返回表示当前日期时间的对象
        LocalDateTime ldt = LocalDateTime.now();
        //2.使用DateTimeFormatter对象来进行格式化//创建DateTimeFormatter对象
        DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy年MM月dd日 HH小时mm分钟ss秒");
        //String format = dateTimeFormatter.format(ldt);
        
        //提供plus 和minus方法可以对当前时间进行加或者减
        //看看890天后,是什么时候把年月日-时分秒
        LocalDateTime localDateTime = ldt.plusDays(890);
        System.out.println("890天以后是"+dateTimeFormatter.format(localDateTime));

        //看看在 3456分钟前是什么时候,把年月日-时分秒输出
        LocalDateTime localDateTime2 = ldt.minusMinutes(3456);
        System.out.println("3456分钟前日期=" + dateTimeFormatter.format(localDateTime2));

    }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是Java连接MySQL数据库的方法: 1. 使用JDBC连接MySQL数据库 ```java import java.sql.Connection; import java.sql.DriverManager; import java.sql.SQLException; public class MySQLConnection { public static void main(String[] args) { // JDBC连接MySQL数据库的URL String url = "jdbc:mysql://localhost:3306/hsp_db02"; // 数据库用户名和密码 String username = "your_username"; String password = "your_password"; try { // 加载MySQL的JDBC驱动程序 Class.forName("com.mysql.jdbc.Driver"); // 建立数据库连接 Connection connection = DriverManager.getConnection(url, username, password); // 连接成功后的操作 System.out.println("成功连接到MySQL数据库"); // 关闭数据库连接 connection.close(); } catch (ClassNotFoundException e) { e.printStackTrace(); } catch (SQLException e) { e.printStackTrace(); } } } ``` 2. 使用连接池连接MySQL数据库(推荐) ```java import java.sql.Connection; import java.sql.SQLException; import javax.sql.DataSource; import org.apache.commons.dbcp2.BasicDataSource; public class MySQLConnectionPool { public static void main(String[] args) { // 创建连接池对象 BasicDataSource dataSource = new BasicDataSource(); // 设置连接池属性 dataSource.setDriverClassName("com.mysql.jdbc.Driver"); dataSource.setUrl("jdbc:mysql://localhost:3306/hsp_db02"); dataSource.setUsername("your_username"); dataSource.setPassword("your_password"); try { // 从连接池获取数据库连接 Connection connection = dataSource.getConnection(); // 连接成功后的操作 System.out.println("成功连接到MySQL数据库"); // 关闭数据库连接 connection.close(); } catch (SQLException e) { e.printStackTrace(); } } } ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值