JAVA基础
文章目录
- JAVA基础
- 变量
- 整形(INT)
- 浮点型(float/double)
- 字符型(char)
- 布尔类型(boolean)
- 基础数据类型的转换
- 自动类型转换
- 强制类型转换
- 重载
- 可变参数
- 基本概念
- 基本语法
- 注意事项和使用细节
- 属性
- 作用域
- 注意事项和细节使用
- javap的使用
- 构造方法(构造器)
- 基本概念
- 注意事项和使用细节
- 对象创建流程分析
- this
- 什么是this
- this的注意害项和使用细节
- 包
- 包的三大作用
- 包的本质分析(原理)
- 包的命名
- 常用的包
- 注意事项和使用细节
- 访问修饰符
- 基本介绍
- 使用的注意事项
- 封装
- 封装介绍
- 封装的理解和好处
- 封装的实现步骤
- 继承
- 继承基本介绍和示意图
- 继承的基本语法
- 继承给编程带来的便利
- 继承的深入讨论/细节问题
- 继承的本质分析
- super关键字
- 基本介绍
- super给编程带来的便利/细节
- super和this的区别
- 方法重写/覆盖
- 基本介绍
- 注意事项和使用细节
- 多态
- 基本介绍
- 多态的具体实现
- 多态注意事项和细节讨论
- 多态的向上转型
- 多态的向下转型
- 属性没有重写之说
- instanceOf
- java动态绑定机制*
- 多态应用
- 多态数组
- 多态参数
- Objcet
- ==运算符
- equals方法
- hashcode
- toString方法
- finalize方法
- 断点调试(debug)
- 一个实际需求
- 断点调试介绍
- 断点调试的快捷键
- java中段
- 类变量(静态变量)
- 什么是类变量
- 如何定义类变量
- 如何访问类变量
- 类变量使用注意事项和细节讨论
- 类方法(静态方法)
- 类方法基本介绍
- 类方法的调用:
- 类方法经典的使用场景
- 类方法使用注意事项和细节讨论
- mian方法
- 深入理解main方法
- 特别提醒
- 代码块/static代码块
- 基本介绍
- 基本语法
- 理解
- 代码块使用注意事项和细节讨论
- 设计模式
- 什么是设计模式
- 什么是单例模式
- 饿汉式
- 懒汉式
- 饿汉式VS懒汉式
- 小结
- final关键字
- 基本介绍
- final使用注意事项和细节讨论
- 抽象类
- 抽象类的介绍
- 抽象类使用的注意事项和细节讨论
- 模板模式
- 接口
- 基本介绍
- 小结
- 注意事项和绍节
- 接口vs继承
- 接口和继承解决的问题不同
- 接口比继承更加灵活
- 接口在一定程度上实现代码解耦
- 接口的多态特性
- 四种内部类
- 基本语法
- 内部类的分类
- 局部内部类的使用
- 匿名内部类的使用(重要!!!)
- 匿名内部类的最佳实践
- 成员内部类
- 静态内部类
- 小结
- 枚举
- 基本内容
- 枚举的两种实现方式
- 自定义类实现枚举
- 使用enum关键字实现枚举
- enum关键字实现枚举注意事项
- 枚举的常用方法
- enum的注意试想和细节讨论
- 注解(Annotation)
- 三个基本的Annotation
- @Override
- @Deprecated
- @SuppressWarnings
- 元注解--JDK的Annotation(了解)
- 元注解的基本介绍
- 元注解的种类
- @Retention
- @Target
- @Documented
- @lnherited注解
- 异常
- 基本概念
- 异常体系图(一部分)
- 小结
- 五大运行时异常
- 编译异常
- 异常处理
- 基本介绍
- 异常处理的方式
- try-catch异常处理
- try-catch实践
- throws异常处理
- 自定义异常
- throw与throws的区别
- 常用类
- 包装类
- 八种包装类
- 包装类与基本数据类型的转换
- 包装类方法
- String类
- String类的理解与创建对象
- 创建String对象的两种方式
- String的特性
- String的常用方法
- String翻转题
- StringBufer类
- 基本介绍
- String Vs StringBuffer
- StringBuffer构造器
- StringBuffer转换
- StringBuffer方法
- StringBuilder
- 基本介绍
- StringBuilder的常用方法
- String、StringBuffer 和StringBuilder的比较
- 效率比较测试
- 选择方式的总结
- Math类
- 基本介绍
- Arrays类
- Arrays的常用方法
- 定制排序源码分析
- 模拟定制排序
- 其他常见方法
- System类
- 常见方法
- Biglnteger类和BigDecimal类
- 基本介绍
- 日期类
- 第一代日期类Date
- 第二代日期类Calendar
- 第三代日期类LocalDateTime
变量
整形(INT)
- Java各整数类型有固定的范围和字段长度,不受具体OS[操作系统]的影响,以
保证java程序的可移植性。 - Java的整型常量(具体值)默认为int型,声明long型常量须后加1或L
- java程序中变量常声明为int型,除非不足以表示大数,才使用long
- 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型更精确。
类型 | 占用存储空间 |
单精度float | 4字节 |
双精度double | 8字节 |
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的使用
- javap是JDK提供的一个命令行工具,javap能对给定的class文件提供的字节代码进行反编译
- 通过它,可以对照源代码和字节码,从而了解很多编译器内部的工作,对更深入地理解如何提高程序执行的效率等问题有极大的帮助。
- 使用格式
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指令反编译看看
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本质的理解:
this的注意害项和使用细节
1.this关键字可以用来访问本类的属性、方法、构造器
2.this用于区分当前类的属性和局部变量
3.访问成员方法的语法:this.方法名(参数列表);
4.访问构造器语法:this(参数列表);注意只能在构造器中使用(即只能在构造器中访问另外一个构造器)
注意:访问构造器语法:this(参数列表);必须放置第一条语句
5.this不能在类定义的外部使用,只能在类定义的方法中使用。
包
包的三大作用
1.区分相同名字的类
2.当类很多时,可以很好的管理类[例如Java API文档]
3.控制访问范围
包的本质分析(原理)
包的本质:实际上就是创建不同的文件夹来保存类文件
包的命名
命名规则:
只能包含数字、字母、下划线、小圆点“.”,但不能用数字开头,不能是关键字或保留字
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来声明继承父类即可。
继承的基本语法
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 ="大头儿子";
}
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. | 区别点 | this | super |
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",18,100);
persons[2] = new Student( "smith",19,30.1);
persons[3] = new Teacher( "scott",30,20000);
persons[4] = new Teacher( "king",50,25000);
//循环遍历多态数组,调用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类变量,在类加载的时候就生成了.
什么是类变量
类变量也叫静态变量/静态属性,是该类的所有对象共享的变量,任何一个该类的对象去访问它时,取到的都是相同的值,同样任何一个该类的对象去修改它时,修改的也是同一个变量。这个从前面的图也可看出来。
如何定义类变量
访问修饰符 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中也可以运行,运行参数在下图添加后运行:
代码块/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反编译来验证)
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 分为两大类:
运行时异常[程序运行时,发生的异常]
编译时异常[编程时,编译器检查出的异常]
异常体系图(一部分)
小结
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
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.有了类的特点,就可以调用类中的方法。
基本数据类型 | 包装类 |
boolean | Boolean |
char | Character |
byte | Byte |
short | Short |
int | lnteger |
long | Long |
float | Float |
double | Double |
包装类与基本数据类型的转换
装箱与拆箱
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 对象可以比较大小]
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.两种方式的内存分布图
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变量开辟空间后,哪怕其复制内容相同,但是地址也不同了。
其原理就是常量的地址时确定的,“=”只是进行一个指针的指向操作,并没有给其分配空间
其原理如下:
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
}
}
老韩小结:底层是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 排序(自然排序和定制排序)
定制排序源码分析
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格式化和解析日期的具体类。它允许进行格式化(日期->文本)、解析(文本->日期)和规范化.
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));
}
}