数据类型:
整数类型:byte(1字节)、short(2字节)、int(4字节)、long(8字节)
浮点型: float(4字节), double(8字节)
布尔型: boolean
1,ASCII码:一个英文字母(不分大小写)占一个字节的空间,一个中文汉字占两个字节的空间。 2,UTF-8编码:一个英文字符等于一个字节,一个中文(含繁体)等于三个字节
按照数据类型分
面向对象
类中属性的使用
属性 — 局部变量
-
相同点:
- 定义变量的格式: 数据类型 变量名 = 变量值
- 先声明,后使用
- 变量都有对应的作用越
-
不同点:
-
2.1 在类中使用的位置不同
属性直接定义在类的一对{}中
局部变量:声明在方法内、方法形参、代码块内、构造器形参、构造器内部的变量
-
2.1 关于权限修饰符的不同
-
属性:可以在声明属性时,指明器权限,使用权限修饰符。
常用的权限修饰符: private、 public、缺省、protected ----> 封装性
-
局部变量: 不可以使用权限权限修饰符
-
-
2.3 默认初始化值的情况:
-
属性: 类的属性,根据其类型,都有默认的初始值。
整型(byte/ shrot/ int /long): 0
浮点型(float / double) : 0.0
字符型 (char) : 0 或 ‘\u0000’
布尔型 (boolean): false
引用数据类型(类、 数组、接口) : null
-
局部变量: 没有默认初始值
意味着,我们在调用局部变量之前,一定要显示赋值
特别的:形参在调用时,我们赋值即可
-
-
2.4 在内存中加载的位置:
- 属性: 加载到堆空间中 (非static)
- 局部变量: 加载到栈空间中
-
class User{
private String name;
public int age;
boolean isMale;
public void talk(String language){ //language:形参,也是局部变量
System.out.pirntln("我们使用" + language + “进行交流”);
}
public void eat(){
String food = "烙饼"; //局部变量
System.out.pirntln("北方人喜欢吃" + food);
}
}
类中方法的声明和使用
方法: 描述类应该具有的功能。
比如: Math类: sqrt()\ random() …
Scanner 类: nextXxx()…
Arrays类: sort() \ binarySearch() \ toString() …
-
举例
public void eat{}
public void sleep(int hour) {}
public String getName(){}
public String getNation(String nation){}
-
方法的声明: 权限修饰符 返回值类型 方法名(形参列表){
方法体
}
-
说明:
-
关于权限修饰符:
Java规定的4中权限修饰符: private/ public / 缺省(default) / protected
-
返回值类型: 有返回值 VS 没有返回值
-
如果有返回值,则必须在方法声明时,指定返回值的类型。同时方法中需要使用
return关键之来指定返回类型的变量或者常量: “return 数据“
如果方法中没有返回值,则方发声明时,使用void来表示。
-
-
方法名: 属性标识符, 遵循标识符的规则和规范, 见明知意
class Customer{
//属性
String name;
int age;
boolean isMale;
//方法
public void eat(){
System.out.println("客户正在吃");
}
}
对象数组的内存解析
方法中的变量都是局部变量,存于栈空间中
类中的属性存在堆空间中
———类和对象的二者的关系和理解
类:抽象的、概念上的内容
对象: 实实在在存在的一个个体。
对象是类派生出来的
———理解“万事万物皆对象”
1.Java语言范畴中,我们都将功能、结构等封装到类中,通过类的实例化,来调用具体的功能结构
>Sacnner String
>文件 : FIle
>网络资源 : URL
2.涉及到Java语言与前端HTML、后端的数据库交互时,前后端的结构在Java层面交互时,都体现为类、对象
匿名对象的使用
- 理解: 我们创建的对象,没有显示的赋给一个变量名,即为匿名对象
- 特征: 匿名对象只能调用一次
- 使用,如下
public class InstanceTest{
public static void main(String[] args){
Phone p = new Phone();
p.sendEmail();
p.playGame();
//匿名对象, 注意:此时是生成了两个对象
new Phone().sendEmail();
new Phone().playGame();
new Phone().price = 1999;
new Phone().showPrice(); // 0.0, 因为两次对象不一样,第二次的值默认为0.0;
}
}
class Phone{
double price;
public void sendEmail(){
System.out.pirnt("发邮件");
}
public void playGame(){
System.out.pirnt("玩游戏");
}
public void showPrice(){
System.out.print("手机价格" + price);
}
}
可变形参
- jdk 5.0 新增的内容
- 具体使用
- 可变个数形参的格式: 数据类型 … 变量名
- 当调用可变个数形参的方法时, 传入的参数个数可以是 : 0/1/2/3、/4、。。。
- 可变个数形参的方法与本类种中方法名相同,形参不同的方法之间的构成重载
- 可变个数形参在方法的形参中,必须声明在末尾,最多可以声明一个可变形参
public class MethodArgsTest{
public static void main(String[] args){
MethodArgsTest test = new MethodArgsTest;
test.show(12);
test.show(new String[]{"AA","BB","CC"});
}
public void show(int i){}
public void show(String s){}
//可变个数形参
public void show(String ... strs){}
public void show(String[] strs){} //以前的写法
}
方法参数的值传递机制(***)
关于变量的赋值:
如果变量是基本数据类型, 此时赋值的是变量所保存的数据值。
如果是引用数据类型,此时赋值的是变量所有保存的数据的地址。
方法的形参传递机制:值传递
1.形参: 方法定义时,声明的小括号内的参数
2.实参: 方法调用时, 实际传递给形参的数据
值传递机制:
如果参数是基本数据类型, 此时实参赋给形参的是实参真是储存的数据值
如果参数是引用数据类型, 此时实参赋给形参的是实参储存数据的地址值
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KvhwXwKd-1678933451143)(C:\Users\冷。。。\AppData\Roaming\Typora\typora-user-images\image-20220309155848555.png)]
尚硅谷211P (值传递机制)
封装与隐藏
问题引入
- 程序设计最求:
- 高内聚: 类的内部数据操作细节自己完成,不允许外部干涉
- 低耦合: 仅对外暴露少量的方法用于使用
- 隐藏对象内部的复杂性,只对外公开简单的接口,便于外界调用,从而提高系统的可扩展性、可维护性。
- 重点private关键字理解
public class AnimalTest{
public static void main(String[] args){
Animal a = new Animal();
a.name = "大黄";
a.age = 1;
a.legs = 4;//当我们想要限制legs数值不能为负数时,可以调用Animal里面的方法setLegs来实现,但是
//如果类Animal里面的legs属性不用关键之private修饰的话,我们依然可以用a.legs=-4;
// 但我们给属性加上 private关键字后,就不能用a.lesg了
a.setLegs(-7);
a.show();
}
}
class Animal{
String name;
int age;
int legs;
public void setLegs(l){
if(l >= 0 && l % 2 == 0){
legs = l;
}else{
legs = 0;
}
}
public void eat(){
System.out.print("动物进食");
}
public void show(){
System.out.pirnt("name = " + name + ", age = " + age + "legs = " + legs);
}
}
封装体现
我们将类的属性xxx私有化(private),同时,提供公共的(public)方法来获取(getxxx)和设置(setxxx)属性的值
封装性的体现,需要权限修饰符来配合(***)
-
Java规定的4中权限(从小到大): private、缺省、protected、 public[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8ZsL9q4k-1678933451144)(C:\Users\ziyi\AppData\Roaming\Typora\typora-user-images\image-20221122152027336.png)]
-
4种权限可以用来修饰类及类的内部结构: 属性、方法、构造器、内部类
-
具体的,4种权限都可以用来修饰类 的内部结构: 属性、方法、构造器、内部类(修饰类的话,只能使用缺省、public)
-
举例实验(封装性练习基本使用)
public class Person{
private int age;
public void setAge( int a){
if( a < 0 || a > 130){
//throw new RuntimeException("传入数据非法")
System.out.print("传入数据非法");
return;
}
age = a;
}
public int getAge(){
return age;
}
}
public class PersonTest{
public static void main(String[] args){
Person p1 = new Person();
//p1.age = 1; 编译不通过,因为属性age是private修饰
p1.setAge(12);
System.out.print("年龄为:" + p1.getAge);
}
}
构造器的使用(尚硅谷p226)
- 构造器的作用:
- 创建对象
- 初始化对象的属性
- 说明:
- 如果没有显示的定义类的构造器,则系统默认提供一个空参的构造器
- 定义构造器的格式: 权限修饰符 类名 (参数列表){}
- 一个类中定义多个构造器,彼此构成重构
- 一旦显示的定义了类的构造器之后,系统就不再提供默认的空参构造器
总结:属性赋值的先后顺序
-
默认初始化
-
显示初始化
-
构造器中赋值
-
通过 ”对象.方法“ 或 “ 对象.属性”的方式赋值
-
1.默认初始化 int age; 属性默认初始化 0 2.显示初始化 int age = 1; 3.构造器中初始化 new Person(1) 4.通过“对象.方法” 或“对象.属性”Person p = new Person(); p.age = 1
以上操作的先后顺序: 1,2,3,4
JavaBean
是一种Java语言写成的可以重用组件
所谓JavaBean, 是指符合如一下标准的Java类:
- 类是公共的
- 有一个无参的公共构造器
- 有属性,并有对应的get/set方法
UML类图
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5oFxPAqf-1678933451144)(C:\Users\ziyi\AppData\Roaming\Typora\typora-user-images\image-20221122160949311.png)]
this 使用
-
this可以用来修饰: 属性、方法、构造器
-
this修饰属性和方法:
- this理解为: 当前对象 或 当前正在创建的对象
- 在类的方法中,我们可以使用“this.属性“ 或”this.方法“,调用当前对象属性或方法。但是,通常情况下,我们都选择忽略"this."。 特殊情况下–》如果方法的形参和类的属性同名时,我们必须显示的使用”this.变量“ 的方式,表明此变量是属性,而非形参。形参需要做到见名知意,所以一般属性和形参同名。
-
this调用构造器
- 我们在类的构造器中,可以显示的使用"this(形参列表)"方式,调用本类中指定的其他构造器
- 构造器不能通过"this(形参列表)"调用自己
- 如果一个类中有N个构造器,则最多有n - 1构造器使用了"this(形参列表)"
- 规定:“this(形参列表)” 必须声明在当前构造器的首行
- 构造器内部,最多只能声明一个"this(形参列表)" , 用来调用其他的构造器
super关键字的使用
- super理解为: 父类的
- super可以调用: 属性、方法、构造器
- super的使用(简言之:在子类中调用被重写过的父类的方法)
- 我们可以在子类的方法或构造器中,通过使用"super.属性"或"super.方法" 的方式,显示的调用父类中声明的属性或者方法。但是通常情况下我们习惯省略"super."
- 特殊情况: 当子类和父类中定义了同名的属性时,我们要想在子类中调用父类中被重写的方法时,则需要显示的使用"super.属性"的方式,表明调用的是父类中声明的属性。
- 特殊情况:与上面属性一样,在调用父类方法时,也是用"super.方法"
- super调用构造器
- 我们可以在子类构造器中显示的使用"super(形参列表)"的方式,调用父类中声明的指定的构造器
- "super(形参列表)"的使用,必须声明在子类构造器的首行
- 在类的构造器中,针对与"this(形参列表)" huo “super(形参列表)” 只能二选一,不能同时出现
- 在构造器的首行,没有显示的声明”this(形参列表) 或 “super(形参列表) ,则默认调用的是父类中空参的构造器super()
- 在类的多个构造器中,至少有一个类的构造器中使用了“super(形参列表), 调用父类中的构造器
- super练习 --尚硅谷277P
面向对象综合练习(***) 235P
MVC设计模式
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nKu3l8oO-1678933451144)(C:\Users\冷。。。\AppData\Roaming\Typora\typora-user-images\image-20220310142556825.png)]
面向对象的特征二:继承性(inheritance)
继承性好处
- 减少代码的冗余,提高了代码的复用性
- 便于功能的扩展
- 为了之后多态性的使用,提供了前提
继承的格式
-
Class A extends B{}
- A:子类、 派生类、subclass
- B: 父类、超类、基类、superclass
-
体现: 一旦子类A继承父类B以后,子类A中就获取了父类B中声明的所有属性和方法。
特别的,父类中声明为private的属性或方法,子类继承父类以后,仍然认为获取了父类中私有的结构。
只有因为封装性的影响,使得子类不能直接调用父类的结构而已。
-
子类继承父类以后,还可以声明自己特有的属性或方法,实现功能的拓展。
Java中关于继承性的规定:
- 一个类可以被多个子类继承,一个类只能有一个父类
- 子类继承父类以后,就获取了直接父类以及所有间接父类中声明的属性和方法
方法的重写(override)
- 重写: 子类继承父类以后,可以父类中同名同参的方法,进行覆盖操作
- 应用: 重写以后,当创建子类对象以后 ,通过子类对象调用子父类中的同名参数的方法时,实际执行的是子类重写父类的方法
- 重写的规定:
- 方法的声明: 权限修饰符---- 返回值类型---- 方法名----(形参列表){方法 体}
- 子类重写的方法的方法名和形参列表与父类被重写的方法的方法名和形参列表相同
- 子类重写的方法的权限修饰符不小于父类被重写的方法的权限修饰符(特殊情况: 子类不能重写父类中声明为private的权限方法)
- 返回值类型 :
- 父类中的返回值类型是void, 则子类重写的方法的返回值也必须是void
- 父类中被重写的方法的返回类型是A类型,则子类中从写的方法的返回值类型可以是A类型,也可以是A类的子类型
- 特别注意,如果父类被重写的方法的返回值类型是基本数据类型,则子类中重写的方法返回值也必须相同
- 子类中重写的方法抛出的异常类型不大于父类中被重写的方法抛出的异常类型
super关键字的使用
- super理解为: 父类的
- super可以调用: 属性、方法、构造器
- super的使用(简言之:在子类中调用被重写过的父类的方法)
- 我们可以在子类的方法或构造器中,通过使用"super.属性"或"super.方法" 的方式,显示的调用父类中声明的属性或者方法。但是通常情况下我们习惯省略"super."
- 特殊情况: 当子类和父类中定义了同名的属性时,我们要想在子类中调用父类中被重写的方法时,则需要显示的使用"super.属性"的方式,表明调用的是父类中声明的属性。
- 特殊情况:与上面属性一样,在调用父类方法时,也是用"super.方法"
- super调用构造器
- 我们可以在子类构造器中显示的使用"super(形参列表)"的方式,调用父类中声明的指定的构造器
- "super(形参列表)"的使用,必须声明在子类构造器的首行
- 在类的构造器中,针对与"this(形参列表)" huo “super(形参列表)” 只能二选一,不能同时出现
- 在构造器的首行,没有显示的声明”this(形参列表) 或 “super(形参列表) ,则默认调用的是父类中空参的构造器super()
- 在类的多个构造器中,至少有一个类的构造器中使用了“super(形参列表), 调用父类中的构造器
- super练习 --尚硅谷277P
子类对象实例化的全过程
-
从结果上来看: (继承性)
子类继承父类以后,就获取了父类中声明的属性和方法
创建子类的对象,在堆空间中,就会加载所有父类中声明的属性。
-
从过程上来看:
当我们通过子类的构造器创建子类对象时,我们一定会直接或间接的调用其父类的构造器,进而调用父类的父类构造器,直到调用了java.lang.Object类中空参的构造器为止。正因为加载过所有的父类的构造器,所以才可以看到内存中有父类中的结构,子类对象才可以考虑进行调用
-
明确: 虽然创建子类对象时,调用了父类的构造器,但是自始至终就创建过一个对象,即为new的子类对象
面向对象的特征三:多态性
多态性
-
理解多态性: 可以理解为一个事务的多种形态
-
何为多态性: 父类的引用指向子类的对象 (Man extends Person)( Person s1 = new Man)
-
多态性的使用: 虚拟方法调用
- 有了对象的多态性以后,我们在编译期,只能调用父类中声明的方法,但是运行期,我们实际执行的是子类重写父类的方法。
- 总结: 编译 看左边(调用的是父类中的方法), 运行看右边 (Person s1 = new Man,运行时运行的是子类中重写过父类方法的方法)
-
多态性的使用前提 :
- 类的继承关系
- 方法的重写
-
对象的多态性: 只适用于方法,不适用于属性(编译和运行都看左边)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Jf8wZnZ9-1678933451145)(C:\Users\ziyi\AppData\Roaming\Typora\typora-user-images\image-20221123161702593.png)]
向下转型的使用instanceof
向上转型: 多态
-
问题引入:
有了对象的多态性以后,内存中实际上是加载了子类特有的属性和方法的,但是由于变量声明为父类类型,导致编译时,只能调用父类中声明的属性和方法。子类特有的属性和方法不能调用
-
如何才能调用子类特有的属性和方法?
使用-----强制类型转符
Person p = new Man();
p.sleep(); 父类的引用指向子类对象,调用子类特有的的方法编译时报错
解决办法,使用向下转型---强制类型转换符
Man m = (Man) p; 此时便可调用子类中特有的属性和方法
-----------注意,此时可能出现类型转换异常。
Woman w = (Woman) p; 此时会出现ClassCastException.(因为此时的p引用是指向Man的,就会出现异常
方法一: 解决方法:
Person p = new Woman;
Woman w = (Woman) p;
方法二: 使用instanceof 判断对象a是否是类A的实例,如果是返回true,否则返回false
例子:
如果对象p 是类Woman的实例,再返回true,则会运行下面结果
if(p instanceof Woman){
Woman w1 = (Woman)p;
w.goShopping();
sout("Woman");
}
- 使用情景: 为了在避免使用向下转型时出现ClassCastException异常,我们在使用向下转型时,会先进行instanceof的判断,如果是true则向下转型,false则不向下转型
- 如果a instanceof A 返回true ,则a instanceof B也返回true(其中B是A的父类)
多态性练习
例子:1
class Base{
int count = 10;
public void display(){
sout(this.count);
}
}
class Sub extents Base{
int count = 20;
public void display(){
sout(this.count);
}
}
public class FieldMethodTest(){
psvm{
Sub s = new Sub();
sout(s.count); //20
s.display(); //20
Base b = s; //多态性
//引用数据类型比较地址值是否相同
sout(b == s); //true
sout(b.count); //10 多态中执行的是子类重写的父类中的方法,属性不能被调用,想要调用必须向下转型
b.display(); // 20
}
}
例子2:
public class InterviewTest2{
psvm(){
Base1 base = new Sub1(); //多态
base.add(1,2,3); //此时结果为 sub_1,因为sub_1才是被重写的方法
Sub1 s = (Sub1)base;//做了向下转型,调用确定个数的参数,所以结果
s.add(1,2,3); //结果是sub_2;
}
}
class Base1{
public void add(int a, int... arr){
sout("base");
}
}
class Sub1 extents Base1{
public void add(int a, int[] arr){
sout("sub_1");
}
public void add(int a, int b, int c){
sout("sub_2");
}
}
Object 类
equals 方法 和 ==
-
回顾 == 的使用
== : 运算符
-
可以使用在基本数据类型变量和引用数据类型变量中
-
如果比较的是基本数据类型变量,比较两个变量保存的数据是否相等。
-
如果比较的是引用数据类型变量: 比较两个对象的地址值是否相同,
注意 : == 左右两边的变量类型必须一致
-
equals() 方法的使用
是一个方法,而非运算符
-
只能使用与引用数据类型
-
Object类中equals()的定义:
public boolean equals(Object obj){
return (this == obj)
} //说明:Object类中定义的equals()和==的作用相同的: 比较两个对象的地址值是否相等
-
像String、Date、File、包装类等都重写了Object类中的equals()方法。重写以后,比较的不是两个引用的地址是否相等,而是比较两个对象的 ”实体内容” 是否相同。
-
通常情况下,我们自定义的类如果使用equals()的话,也通常是比较两个对象的 “实体内容” 是否相同。 那么,我们就需要对Object类中的equals()进行重写
重写原则: 比较两个对象的实体内容是否相同。
重写例子
//手动重写Object的equals方法
public boolean equals(Object obj){
if(this == obj){
return true;
}
if(obj instanceof Customer){
Customer cust = (Customer) obj;
if(this.age == cust.age && this.name.equals(cust.name)){
return true;
}
}
return false;
}
//注意重要细节
String a = "111";
String b = "111";
a == b; true //此时存在于常量池中,直接复用同一个
String a = new String("111");
String b = new String("111");
a == b; false //此时比较两个地址不一样
a.equals(b);true //此时String中的equals是被重写过的,比较的是内容是否相等
toString方法
Object类中toString()的使用
- 当我们输出一个对象的引用时,实际上就是调用当前对象的toString()
- Object类中tostring()的定义:
public String toString(){
return getClass().getName() + "@" +Integer.toHexString(hashCode);
}
- 像String、Date、File、包装类等都重写了Object类中的toString()方法,使得在调用对象的toString()时,返回"实体内容"信息
- 自定义类也可以重写toString方法,调用时返回此对象的实体内容
包装类
-
基本数据类型转换为包装类: Integer in1 = new Integer(12);
-
包装类转为基本数据类型: int in2 = in1.intValue();
-
JDK 5.0以上: 实现了自动装箱和自动拆箱
- 注意JDK5.0以前基本数据类型到包装类,需要手动new构造器进行装箱(int num = 10; Integer num1 = new Integer(num))
- 自动装箱: 基本数据类型 —>包装类
int num2 = 10;
Integer in1 = num2;
-
- 自动拆箱: 包装类---->基本数据类型
int num3 = in1;
String类型和基本数据类型、包装类相互转
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5CYr5M2E-1678933451145)(C:\Users\ziyi\AppData\Roaming\Typora\typora-user-images\image-20221124171935521.png)]
+++++++//String 类型---》基本数据类型、包装类: 调用包装类的parseXxx(String s)
public void test5(){
String str1 = "123";
//错误情况
//int num1 = (int)str1;
// Integer in1 = (Integer)str1;
int num2 = Integer.parseInt(str1);
System.out.print(num2 + 1);
}
+++++++//基本数据类型、包装类---》Stirng类型: 调用String重载的valueOf(Xxx xxx)
public void test4(){
int num1 = 10;
//方式一: 连接运算
String str1 = num1 + "";
//方式二: 调用String的valueOf(Xxx xxx)
float f1 = 12.3f;
String str2 = String.valueOf(f1);
}
class Order{
boolean male; 基本数据类型 初始化默认值false
Boolean female; 包装类 初始化默认值为null
}
***********************************************************
public void Test() {
//输出的结果为1.0 ,因为在编译时,会自动将数据类型向上统一
Object o = true ? new Integer(1) : new Double(2.0);
System.out.println(o);
}
重要面试题
@Test
public void test8(){
Integer i = new Integer(1);
Integer j = new Integer(1);
System.out.println(i == j); //false
//Integer内部定义了IntegerCache结构,IntegerCache中定义了Integer[]
//保存了从-128~ 127范围的整数,如果我们使用自动装箱的方式,给Integer赋值的范围在
//-128~127范围内时候,可以直接使用数组中的元素,不用再去new了,目的: 提高效率
Integer m = 1;
Integer b = 1;
System.out.println(m == b); //true
Integer x = 128; //相当于new了一个integer
Integer y = 128; //相当于new了一个integer
System.out.println(x == y); //false
}
面向对象包装类练习308P
设计模式
-
理解:
设计模式是在大量的实践中总结和理论化之后优选的代码结构、编程风格、以及解决问题的的思考方式。
-
常用的设计模式 — 23种经典的设计模式
创建型模式: 5种 工厂方法模式、抽象工厂模式、单例模式、建造者模式、原型模式
。。。。。。。
单例设计模式
要解决的问题:
所谓的单例设计模式,就是采取一定的方法保证在整个的软件系统中,对某个类只能存在一个对象实例
-
饿汉式
//所谓的单例设计模式,就是采取一定的方法保证在整个的软件系统中,对某个类只能存在一个对象实例 public class SingletonTest1{ public static void main(String[] args){ Bank bank1 = Bank.getInstance(); Bank bank2 = Bank.getInstance(); System.out.println(bank1 == bank2); } } class Bank{ //私有化类的构造器 private Bank(){ } //2.内部创建类的对象 //4.要去此对象必须声明为静态的 private static Bank instance = new Bank(); //3.提供公共的静态的方法,返回类的对象 public static Bank getInstance(){ return instance; } //此种方式也可以实现单利模式 class Bank1{ private Bank(){} public static final Bank1 instance1 = new Bank1(); } }
-
懒汉式
class Shop{ //1.私有化类的构造器 private Shop(){ } //2.声明当前类对象,没有初始化 //4.此对象也必须声明为static的 private static Shop instance = null; //3.声明public、static的返回当前类对象的方法 public static Shop getInstance(){ if(instance == null){ instance = new Shop(); } return instance; } }
单例设计模式的应用场景:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RY9VI2aA-1678933451145)(C:\Users\ziyi\AppData\Roaming\Typora\typora-user-images\image-20221125153014225.png)]
代理模式
package bank;
/**
* @author xuec
* @date 2022/11/29
**/
public class NetWorkTest {
public static void main(String[] args) {
Server server = new Server();
ProxyServer proxyServer = new ProxyServer(server);
proxyServer.browse();
}
}
interface NetWork{
public void browse();
}
//被代理类
class Server implements NetWork{
@Override
public void browse() {
System.out.println("真实的服务器访问网络");
}
}
//代理类
class ProxyServer implements NetWork{
private NetWork work;
public ProxyServer(NetWork work){
this.work = work;
}
public void check(){
System.out.println("联网之前的检查操作");
}
@Override
public void browse() {
check();
work.browse();
}
}
关键字的使用
static
- static: 静态的
- static可以用来修饰: 属性、方法、代码块、 内部类
- 使用static修饰属性: 静态变量 或 类变量
- 属性:按是否使用static修饰, 分为: 静态属性 vs 非静态属性(实例变量)
- 实例变量: 我们创建了类的多个对象,每个对象都独立拥有一套类中的非静态属性。当修改其中一个对象中的非静态属性时候,不会导致其对象中同样的属性值的修改
- 静态变量: 我们创建了类的多个对象,多个对象共享一个静态变量。当通过某一个对象修改静态变量时,会导致其他对象调用此静态变量时,是修改过了的。
- static修饰属性的其他说明:
- 静态变量随着类的加载而加载。可以通过 “类.静态变量” 的方式进行调用
- 静态变量的加载要早于对象的创建
- 由于类只会加载一次,则静态变量在内存中也会存在一份,存在方法区的静态域中。
- 静态属性距离: System.out ; Math.PI;
- 属性:按是否使用static修饰, 分为: 静态属性 vs 非静态属性(实例变量)
- 使用static修饰方法: 静态方法
- 随着类的加载而加载, 可以通过 "类.静态方法"的方式进行调用
- 静态方法中,只能调用静态的方法或属性
- 非静态方法中,既可以调用非静态的方法或属性,也可以调用静态的方法或属性
- static注意点:
- 在静态的方法内,不能使用this关键字、super关键字
- 关于静态属性和静态方法的使用,大家都从生命周期的角度去理解
- 开发中,如何确定一个属性是否声明为static的
- 属性是可以被多个对象共享,不会随着对象的不同而不同
- 开发中,如何确定一个方法是否声明为static的?
- 操作静态属性的方法,通常设置为static
- 工具类中的方法,习惯上声明为static的,比如: Math、Arrays、Collections
类变量 VS 实例变量内存解析
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KcjK7dos-1678933451146)(C:\Users\ziyi\AppData\Roaming\Typora\typora-user-images\image-20221125143928140.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9VjTxqlN-1678933451146)(C:\Users\ziyi\AppData\Roaming\Typora\typora-user-images\image-20221125151319485.png)]
main()方法的使用说明
- main()方法作为程序的入口
- main()方法也是一个普通的静态方法
代码块
- 代码块的作用: 用来初始化类、对象
- 代码块如果有修饰的话,只能使用static
- 分类: 静态代码块 VS 非静态代码块
- 静态代码块:
- 内部 可以有输出语句
- 随着类的加载而执行,而且只执行一次
- 作用: 初始化类的信息
- 如果一个类中定义了多个静态代码块,则按照声明的先后顺序执行
- 非静态代码块
- 内部可以有输出语句
- 随着对象的创建而执行
- 每创建一个对象,就执行一次非静态代码块
- 作用,可以在创建对象时,对对象的属性等进行初始化
final关键字
-
final可以用来修饰的结构: 类、方法、变量
-
final 用来修饰的类,此类不能被其他类继承
比如String类、 System类、StringBuffer类
-
final 用来修饰方法: 此方法不能被重写
-
final 用来修饰变量: 此时的 “变量” 称为 “常量 ”
- final修饰属性: 可以考虑的赋值位置有: 显示初始化、代码块中初始化、构造器中初始化
- final修饰局部变量:
- 尤其是使用final修饰形参时,表明此形参是一个常量。当我们调用此方法时,给常量形参赋一个实参,一旦赋值以后,就只能在方法体内使用此形参,但不能进行重写赋值
static final 用来修饰属性: 全局常量
-
类的属性赋值的位置有哪些,先后顺序
默认初始化
显示初始化、代码块中初始化
构造器中初始化
通过 “对象.属性” 或 “对象.方法” 的方式赋值
抽象类和抽象方法
abstract关键字的使用
-
abstract: 抽象的
-
abstract可以用来修饰的结构: 类、 方法
-
abstract修饰类: 抽象类
- 此类不能实例化
- 抽象类中一定有构造器,便于子类实例化时调用(涉及: 子类对象实例化全过程)
- 开发中,都会提供抽象类的子类,让子类对象实例化,完成先关操作
-
abstract修饰方法: 抽象方法
-
抽象方法只有方法的声明,没有方法体
-
包含抽象方法的类,一定是一个抽象类。反之,抽象类中可以没有抽象方法
-
若子类重写了父类中的所有抽象方法后,此子类方可实例化
若子类没有重写父类中的所有的抽象方法,则此子类也是一个抽象类,需要使用abstract修饰
-
-
使用注意点
- abstract不能用来修饰: 属性、构造器等结构
- abstract不能用来修饰私有方法、静态方法、final方法、final的类
练习:344P
---------------模板方法设计模式-------------------
package bank;
public class TemplateTest{
public static void main(String[] args) {
test t = new test();
t.spendTime();
}
}
abstract class TemplateSpend{
public abstract void code();
public void spendTime(){
long start = System.currentTimeMillis();
this.code(); //不确定部分
long end = System.currentTimeMillis();
System.out.println("花费时间:" + (end - start));
}
}
class test extends TemplateSpend{
@Override
public void code(){
for (int i = 0; i < 1000; i++) {
System.out.println(i);
}
}
}
接口interface
JDK7
接口的使用
-
接口使用interface来定义
-
Java中,接口和类是并列的两个结构
-
如何定义接口: 定义接口中的成员
- JDK 7以前,只能定义全局常量和抽象方法
- 全局常量: public static final的,但是书写时,可以省略不写
- 抽象方法 : public abstract
- JDK 7以前,只能定义全局常量和抽象方法
-
接口中不能定义构造器,意味着接口不能实例化
-
Java开发中,接口通过让类去实现(implements) 的方式来使用
- 如果实现类覆盖了接口中的所有抽象方法,则此实现类就可以实例化
- 如果实现类没有覆盖接口中所有的抽象方法,则此实现类仍为一个抽象类
-
Java类可以实现多个接口 —> 弥补了Java单继承性的局限性
格式: class AA extends BB implements CC, DD, EE
-
接口与接口之间可以继承,并且可以多继承
-
接口的具体使用,体现多态性
-
接口,实际上可以看作一种状态
实例-------注意几种匿名和非匿名的用法
package bank;
/**
* @author xuec
* @date 2022/11/29
**/
public class USBTest {
public static void main(String[] args) {
Computer com = new Computer();
//1、创建了接口的非匿名实现类非匿名对象
Flash flash = new Flash();
com.transferData(flash);
//2、创建了接口的非匿名实现类的匿名对象
com.transferData(new Printer());
//3、创建了接口的匿名实现类非匿名对象
USB phone = new USB() {
@Override
public void start() {
System.out.println("手机开始工作");
}
@Override
public void stop() {
System.out.println("手机结束工作");
}
};
com.transferData(phone);
//4.创建了接口的匿名实现类的匿名对象
com.transferData(new USB() {
@Override
public void start() {
System.out.println("MP3开始工作");
}
@Override
public void stop() {
System.out.println("MP3结束工作");
}
});
}
}
class Computer{
public void transferData(USB usb){
usb.start();
System.out.println("具体传输数据细节");
usb.stop();
}
}
interface USB{
public void start();
public void stop();
}
class Flash implements USB {
@Override
public void start() {
System.out.println("闪盘开始工作");
}
@Override
public void stop() {
System.out.println("闪盘结束工作");
}
}
class Printer implements USB{
@Override
public void start() {
System.out.println("打印机开始工作");
}
@Override
public void stop() {
System.out.println("打印机结束工作");
}
}
代理模式
interface面试拍错题
interface A{
int x = 0;
}
class B {
int x = 1;
}
class C extends B implement A {
public void px(){
//编译不通过,因为X是不确定的
//System.out.println(x);
System.out.println(super.x);//1
System.out.println(A.x);//0
}
public static void main(String[] args){
new C().px();
}
}
面试题二:
interface Playable{
void play();
}
interface Bounceable{
void play();
}
interface Rollable extends Playable,Bounceable{
Ball ball = new Ball("PingPang");
}
class Ball implements Rollable{
private String name;
public String getName(){
return name;
}
public Ball(String name){
this.name = name;
}
public void play(){
ball = new Ball("Football"); //此处可以直接用,Ball类实现了Rollable接口,获得此接口的所有结构,但是不能再new Ball("Football"); 因为接口中的Ball是被public static final修饰的,为全局常量不能改变
System.out.println(ball.getName());
}
}
interface、Java JDK8 新特性
除了定义全局常量和抽象方法外,还可以定义静态方法、默认方法
public interface CompareA{
//静态方法
public static void method1(){
System.out.println("CompareA: 北京");
}
//默认方法
public default void method2(){
System.out.println("CompareA: 上海");
}
// 默认方法
default void method3(){
System.out.println("CompareA : 上海");
}
}
public class SubClassTest{
public static void main(String[] args) {
SubClass s = new SubClass();
//s.method1();
//SubClass.method1();
// 注意 1: 接口中定义的静态方法,只能通过接口来调用,不然报错
CompareA.method1();
// 注意 2: 通过实现类的对象, 可以调用接口中的默认方法
S.method2(); //静态方法
// 注意 3: 如果子类(或实现类) 继承的父类和实现的接口中声明了同名同参数的方法,
// 那么子类在没有重写此方法的情况下,默认调用的是父类中的同名同参数的方法。
// 注意 4: 如果实现类中实现了多个接口,而这多个接口中定义了同名同参数的默认方法,
// 那么在实现类没有重写此方法的情况下,报错 --> 接口冲突
s.methond3();//静态方法
}
}
class SubClass implements CompareA,CompareB{
}
内部类
类的内部成员之五 : 内部类
- Java中允许讲一个类A声明在另一个类B中, 则类A就是内部类,类B称为外部类
- 内部类的分类: 成员内部类(静态、非静态) VS 局部内部类(方法内、代码块内、构造器内)
- 成员内部类:
- 一方面: 作为外部类的成员
- 调用外部类的结构
- 可以被static修饰
- 可以被4中不同的权限修饰
- 另一方面,作为一个类
- 类内可以定义属性、方法、构造器
- 可以被final修饰,表示此类不能被继承,不修饰则可以被继承
- 可以被abstract修饰
- 一方面: 作为外部类的成员
- 关注一下的3个问题
- 如何实例化成员内部类的对象
- 如何在成员内部类中区分调用外部类的结构
- 开发中局部内部类的使用
异常处理
异常体系结构
java.lang.Throwable
|----java.lang.Error: 一般不编写针对性的代码进行处理
|----java.lang.Exception: 可以进行异常处理
|----编译时异常(checked)
|----IOException
|----FileNotFoundException
|----ClassNotFoundException
|----运行时异常(unchecked)RuntimeException
|----NullPointerException
|----ArrayIndexOutOfBoundsException
|----ClassCastException
|----NumberFormatException
|----ArithmeticException算数异常
异常处理方式一
抓抛模型:
过程一:
”抛“:程序正常执行的过程中,一旦出现异常,就会在异常代码处生成一个对应异常类对象。并将此对象抛出,一旦抛出对象以后,其后的代码就不会执行。
关于异常对象的产生: 1.系统自动生成的异常对象
2.手动生成一个异常对象,并抛出(throw)
过程二:
“抓”,可以理解为异常的处理方式:a — try-cathc-finally
b — throws
-
try- catch- finally的使用
try{ //可能出现异常代码 }catch(异常类型1 变量名1){ //处理异常的方式 }catch(异常类型2 变量名2){ //处理异常的方式 }catch(异常类型3 变量名3){ //处理异常的方式 } ...... finally{ //一定会执行的代码 } 说明: 1.finally是可选的 2.使用try将可能会出现异常的代码包装起来,在执行过程中,一旦出现异常,就会生成一个对应异常类的对象,根据此对象的类型,去catch中进行匹配。 3.一旦try中的异常对象匹配到某个catch时,就进入catch中进行异常的处理。一旦处理完成,就跳出当前的try-catch结构(在没有finally的情况)。继续执行其后的代码 4.catch中的异常类型如果没有子父类关系,则声明前后没有关系 catch中的异常类型如果有子父类关系,则子类一定声明在父类的上面,不然报错。 5.常用的异常对象处理方式: a.String getMessage() b. printStackTrance() finally的常用范围:像连接数据库、输入输出流、网络编程Socket等资源,JVM是不能自动回收的,我们需要自己手动进行资源的释放。此时的资源释放,就需要声明在finally中(如下面的重要例子)
finally使用说明名:—重要
public void test(){ FileInputStream fis = null; try{ File file = new File("hello.txt"); fis = new FileInputStream(file); int data = fis.read(); while(data != -1){ System.out.print((char)data); data = fis.read(); } }catch(FileNotFoundException e){ e.printStackTrace(); }finally{ try{ if(fis != null) fis.close(); }catch(IOException e){ e.printStackTrace; } } }
体会1:-- 使用try-catch-finally处理编译时异常,使得程序在编译时就不再报错,但是运行时任然可能会报错。相当于我们使用try-catch-finally将一个编译时可能出现的异常,延迟到运行时后出现。
体会2:-- 开发中,由于运行时异常比较常见,所以我们通常就不针对运行时异常编写try-catch-finally了。但是编译时异常是需要处理的。
异常处理方式二
throws + 异常类型
-
throws + 异常类型 卸载方法的声明处。指明此方法执行时,可能会抛出的异常类型。
一旦当方法执行时,出现异常,仍会在异常代码处生成一个异常类的对象,此对象满足throws后异常类型时,就会被抛出。 异常代码后续的代码,就不在执行
-
体会:
- try-catch-finally 真正的将异常给处理了
- throws 的方式只是将异常抛给了方法的调用者。 并没有真正将异常处理 Ctrl+T
开发中如何选择
开发中使用try-catch-finally还是用throws?
情况1: 如果父1·rows方式处理异常,则子类重写的方法也不能使用throws,意味着如果子类重写的方法中有异常,必须使用try-catch-finally方式处理。
情况2: 执行的方法a中,先后又调用了另外的几个方法,这几个方法是递进关系执行的。则建议这几个方法使用throws方式处理异常。而执行的方法a可以考虑使用try-catch-finally方式处理。
线程
基本概率
线程 创建和使用***
方式一 :继承Thread类
- 创建一个继承与Thread类的子类
- 重写Thread类的run()方法 --》 将此线程执行的操作声明在run()中
- 创建Thread类的子类对象
- 通过此对象调用start()
class MyThread extends Thread{
@Override
public void run(){
for(int i=0; i < 100; i++){
sout(i);
}
}
}
public class ThreadTest{
public static void main(String[] args){
//3.创建Thread类的子类对象
MyThead t1 = new MyThead();、
//4.通过此对象调用start(),启动当前线程 ,调用当前线程run()
t1.start();
// 问题一: 不能通过直接调用run()的方式启动线程
//t1.run();
//问题二: 再一次启动一个线程,不可以在用t1.start()的线程去执行,会报IllegalThread
//我们需要从新创建一个线程的对象
MyThread t2 = new MyThead();
t2.start();
}
}
测试Thread中的常用方法:
测试Thread中的常用方法:
- start(): 启动当前线程;调用当前线程的run()
- run(): 通常需要重写Thread类中的此方法,将创建的线程要执行的操作声明在此方法中
- currentThread(): 静态方法,返回执行当前代码的线程
- getName(): 获取当前线程的名字
- setName(): 设置当前线程的名字
- yield(): 释放当前CPU的执行权
- join(): 在线程a中调用线程b 的join(), 此时线程a就进入阻塞状态,直到线程完全执行完以后,线程a 才结束阻塞状态
- stop(): 已过时,当执行此方法时,强制结束当前线程。
- sleep(long millitime): 让当前线程 ,指定的millitime毫秒, 在指定的millitime毫秒时间内,当前线程是阻塞状态
- isAlive(): 判断当前线程是否存活
线程的优先级:
设置级别:
MXA_PRIORITY 10
MIN_PRIORITY 1
NORM_PRIORITY 5 —>默认优先级
getPriority : 获取当前线程级别
setPriority: 设置线程优先级
说明: 高优先级的线程会抢占低优先级线程CPU的执行权。但是只是意味着高级别的线程被优先执行的概率更高,并不是高优先级的先执行完在执行低优先级。
使用继承Thread方式实现3窗口卖票------重要例子注意细节
class Windows extends Thread{
//此处这样定义会出现卖300张票,创建了三个对象,每个对象100张票
// private int ticket = 100;
//1.需要改为如下,改为静态变量,这样独有一份,三个线程都在此静态变量中取值
//2.但是还存在线程安全问题
private static int ticket = 100;
@override
public void run(){
while(true){
if(ticket > 0){
System.out.pirntln(getName() + ": 卖票,票号为:" + ticket)
}else
break;
}
}
}
public class WindowsTest{
public static void main(String[] args){
Windows w1 = new Windows();
Windows w2 = new Windows();
Windows w3 = new Windows();
w1.setName("窗口1");
w2.setName("窗口2");
w3.setName("窗口3")
w1.start();
w2.start();
w3.start();
}
}
方式二: 实现Runnable接口的类
- 创建一个实现了Runnable接口的类
- 实现类去实现Runnable中的抽象方法: run()
- 创建实现类的对象
- 将此对象作为参数传递到Thread类的构造器中,创建Thread类的对象
- 通过Thread类的对象调用start()
//1.创建一个实现了Runnable接口的类
class MThread implement Runnable{
//2. 实现类去实现Runnable中的抽象方法:run()
@Override
public void run(){
}
}
public class ThreadTest{
public static void main(String[] args){
//3. 创建实现类的对象
MThread mThread = new MThread();
//4.将此对象作为参数传递到Thread类的构造器中,创建Thread类的对象
Thread t1 = new Thread(mThread);
//5.通过Thread类的对象调用start()
t1.start();
//在启动一个线程
Thread t2 = new Thread(mThread);
t2.start();
}
}
使用实现Runnable接口方式实现3窗口卖票------重要例子注意细节
package thread;
import sun.plugin2.os.windows.Windows;
/**
* @author XueC.Chen
* @date 2022/11/11
* @apiNote
*/
public class WindowsTest{
public static void main(String[] args){
Windows1 win = new Windows1();
Thread t1 = new Thread(win);
Thread t2 = new Thread(win);
Thread t3 = new Thread(win);
t1.setName("窗口1");
t2.setName("窗口2");
t3.setName("窗口3");
t1.start();
t2.start();
t3.start();
}
}
class Windows1 implements Runnable {
private int ticket = 100;
@Override
public void run() {
while (true) {
if (ticket > 0) {
System.out.println(Thread.currentThread().getName() + ":卖票,票号为:" + ticket);
ticket--;
} else {
break;
}
}
}
}
两种方式的区别:
开发中: 优先选择: 实现Runnable接口的方式
原因:
- 实现的方式没有类的单继承局限性
- 实现的方式更适合来处理多个线程有共享数据的情况
联系: public class Thread implements Runnable
相同的: 两种方式都需要重写run(),将线程要执行的逻辑声明在run()中。
线程的生命周期
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-dpRShh8K-1678933451146)(C:\Users\ziyi\AppData\Roaming\Typora\typora-user-images\image-20221130111600533.png)]
说明:
-
生命周期关注两个概念: 状态、相应的方法
-
关注: 状态a —> 状态b : 那些方法执行了(回调用方法)
某个方法主动调用: 状态a —> 状态b
-
阻塞: 临时状态,不可以作为最终状态
死亡: 最终状态
线程同步安全问题***
例子:创建三个窗口卖票,总共100张,使用实现Runnable接口方式
1.问题: 卖票过程中出现重票、错票 —》 线程安全问题
2.问题出现的原因: 当某个线程操作车票的过程中,尚未操作完成时,其他线程参与进来,也操作车票。
3.如何解决:当一个共享数据在操作ticket时候,其他线程不能参与进来,只有当前线程a操作完成后其他线程才可以操作。即使线程a处于阻塞状态也不能被改变。
方式一: 同步代码块
synchronized(同步监视器){
//需要同步的代码块
}
说明: 1、操作共享数据的代码,即为需要被同步的代码
2、共享数据: 多个线程共同操作的变量
3、同步监视器,俗称: 锁。任何一个类的对象,都可以充当锁。
补充:
1、在实现Runnable接口创建多线程的方式中,我们可以考虑使用this充当当前同步监视器
2、如果是继承Thread实现多线程的方式中,我们谨慎使用this充当同步监视器,因为此时的this,可能是多个对象。我们可以使用类来充当(Window1.class)
实例
class Window1 implements Runnable{
private int ticket = 100;
@Override
public void run(){
while(true){
synchronized(this){ //this代表当前类的对象,即w
if(ticket > 0){
try{
Thread.sleep(100);
} catch (InterruptedException e){
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"卖票,票号"+ticket);
ticket--;
}else{
break;
}
}
}
}
}
public class WindowTest{
public static void main(String[] args){
Window1 w = new Window1();
Thread t1 = new Thread(w);
Thread t2 = new Thread(w);
Thread t3 = new Thread(w);
t1.start();
t1.start();
t1.start();
}
}
方式二:同步方法
如果需要被同步的代码块在一个完整的方法中,则可以采用同步方法。
实例一: 实现Runnable接口的多线程--------同步
class Window1 implements Runnable{
private int ticket = 100;
@Override
public void run(){
while(true){
show();
}
}
}
private synchronized void show(){ //同步监视器: this
if(ticket > 0){
try{
Thread.sleep(100);
} catch (InterruptedException e){
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"卖票,票号"+ticket);
ticket--;
}
}
}
public class WindowTest{
public static void main(String[] args){
Window1 w = new Window1();
Thread t1 = new Thread(w);
Thread t2 = new Thread(w);
Thread t3 = new Thread(w);
t1.start();
t1.start();
t1.start();
}
}
实例二:通过继承Thread的方式实现多线程安全----同步方法
class Window1 extends Thread{
private static int ticket = 100;
@Override
public void run(){
while(true){
show();
}
}
}
private static synchronized void show(){ //同步监视器: Window1
//private synchronized void show(),同步监视器: t1,t2,t3
if(ticket > 0){
try{
Thread.sleep(100);
} catch (InterruptedException e){
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"卖票,票号"+ticket);
ticket--;
}
}
}
public class WindowTest{
public static void main(String[] args){
Window1 t1 = new Window1();
Window1 t2 = new Window1();
Window1 t3 = new Window1();
t1.start();
t2.start();
t3.start();
}
}
同步方法总结:
- 同步方法仍然涉及到同步监视器,只是不需要我们显示的声明
- 非静态的同步方法,同步监视器: this
- 静态的同步方法,同步监视器: 当前类本类
使用同步机制将懒汉式改为线程安全
//懒汉式: 线程不安全
Bank{
private Bank();
private static Bank instance = null;
public static Bank getInstance(){
if(instance == null){
instance = new Bank();
}
return instance;
}
}
//懒汉式: 线程安全
Bank{
private Bank();
private static Bank instance = null;
public static Bank getInstance(){
//方式一: 效率不高
synchronized(Bank.class){
if(instance == null){
instance = new Bank();
}
}
return instance;
}
}
//方式二:
Bank{
private Bank();
private static Bank instance = null;
public static Bank getInstance(){
//方式一: 效率不高
//下面这种方式稍高
if(instance == null){
synchronized(Bank.class){
if(instance == null){
instance = new Bank();
}
return instance;
}
}
}
}
方式三:Lock
-
面试题:synchronized 与 Lock的异同
-
相同:二者都可以解决线程安全问题
-
不同: synchronized机制在执行完相应的同步代码以后,自动的释放同步监视器
Lock需要手动的启动同步,同时结束同步也需要手动实现
-
-
优先使用顺序:
Lock — 同步代码块—同步方法
-
面试题: 如何解决线程安全问题?有几种方式
- 有三种方式: Lock ,synchronized同步代码块, 同步方法
package thread;
import com.sun.corba.se.impl.orbutil.LogKeywords;
import com.sun.org.apache.bcel.internal.generic.NEW;
import java.util.concurrent.locks.ReentrantLock;
/**
* @author XueC.Chen
* @date 2022/11/11
* @apiNote
*/
public class LockTest {
public static void main(String[] args) {
Windows3 thread = new Windows3();
Thread t1 = new Thread(thread);
Thread t2 = new Thread(thread);
Thread t3 = new Thread(thread);
t1.setName("窗口1");
t2.setName("窗口2");
t3.setName("窗口3");
t1.start();
t2.start();
t3.start();
}
}
class Windows3 implements Runnable{
private int ticket =100;
//1.实例化ReentrantLock
private ReentrantLock lock = new ReentrantLock();
@Override
public void run() {
while (true){
try {
//2.使用锁定方法lock()
lock.lock();
if ( ticket > 0){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+ "卖票: 票号为:" + ticket);
ticket --;
}else{
break;
}
}finally {
// 3.调用解锁方法: unlock()
lock.unlock();
}
}
}
}
线程的死锁问题
线程的通信
线程通信例子:使用两个线程打印 1-100 , 线程1 、线程2 交替打印
涉及的三个方法:
wait(): 一旦执行此方法,当前线程就进入阻塞状态,并释放同步监视器
notify():一旦执行此方法,就会唤醒被wait的一个线程,如果有多个线程被wait,就唤醒优先级最高的线程
notifyAll():一旦执行此方法,就会唤醒所有被wait的线程
说明:
- wait() 、 notify() 、 notifyAll() 三个方法必须使用在同步代码块或同步方法中
- 三个方法调用者必须是相同的同步监视器,否则会出现IllegalMonitorStateException异常
- 三个方法都是定义在java.lang.Object类中
面试题: sleep() 和 wait() 的异同
- 相同点: 一旦执行方法,都可以使得当前的线程进入阻塞状态
- 不同点:
- 两个方法声明的位置不同: Thread类中声明sleep(),Object类中声明wait()
- 调用的要求不同: sleep()可以在任何需要的场景下调用。wait()必须使用在同步代码块或同步方法中
- sleep()不会释放锁,wait()会释放同步锁
常用类
String类的概述
String的使用
String: 字符串,使用一对 “” 引用起来
- String声明为final的,不能被继承
- String实现类Serializable接口,表示String可以比较大小
- String内部定义了final char[] value 用于存储字符串数据
- String代表不可变的字符序列。简称: 不可变性
体现:1. 当对字符串从新赋值时,需要重写执行内存区域赋值,不能使用原来的value进行赋值。
2.当对现有的字符串进行连接操作时,也需要重新指定内存区域赋值,不能使用原有的value赋值。
3.当调用String的replace()方法时,也需要重新指定内存区域赋值,不能使用原有的value赋值。
- 通过字面量的方式(区别于new)给一个字符串赋值,此时的字符串值声明在字符串常量池中。
- 字符串常量池中是不会存储相同内容的字符串的。
@Test
public void test1{
String s1 = "abc"; //字面量的定义方式
String s2 = "abc";
s1 = "hello";
System.out.println(s1 == s2); //比较s1和s2的地址值
sout(s1); //hello
sout(s2); //abc
String s3 = "abc";
s3 += "def";
sout(s3); //abcdef
sout(s2);
String s4 = "abc";
String s5 = s4.replace('a', 'm');
sout(s4); // abc
sout(s5); //mbc
}
String的实例化方式
方式一: 通过字面量定义的方式
方式二: 通过new + 构造器方式
@test
public void test2{
//通过字面量定义的方式: 此时s1和s2的数据javaEE声明在方法区中的字符串常量池中
String s1 = "javaEE";
String s2 = "javaEE";
//通过new + 构造器的方式: 此时的s3和s4保存的地址值,是数据在堆空间中开辟空间以后对应的地址值
String s3 = new String("javaEE");
String s4 = new String("javaEE");
sout(s1 == s2); //true
sout(s1 == s3); //falses
sout(s1 == s4); //false
sout(s3 == s4); //false
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0E6AJVfV-1678933451147)(C:\Users\ziyi\AppData\Roaming\Typora\typora-user-images\image-20221130144836771.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3xbwAKzw-1678933451147)(C:\Users\ziyi\AppData\Roaming\Typora\typora-user-images\image-20221130144743048.png)]
@Test3
public void test3{
Person p1 = new Person("tom",12);
Person p2 = new Person("tom",12);
sout(p1.name.equals(p2.name);// true
sout(p1.name == p2.name); //true
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-gJxq8oo1-1678933451147)(C:\Users\ziyi\AppData\Roaming\Typora\typora-user-images\image-20221130150503635.png)]
面试题细节
String s = new String("abc"); 方式创建对象,在内存中创建了几个对象?
答案: 创建了两个,一个是堆空间new 的结构, 另一个是char[]对应的常量池中的数据: “abc“
String 不同拼接操作
@Test
public void test4{
String s1 = "javaEE";
String s2 = "hadoop";
String s3 = "javaEEhadoop";
String s4 = "javaEE" + "hadoop";
String s5 = s1 + "hadoop";
String s6 = "javaEE" + s2;
String s7 = s1 + s2;
sout(s3 == s4); //true
sout(s3 == s5); //false
sout(s3 == s6); //false
sout(s3 == s7); //false
sout(s5 == s6); //false
sout(s5 == s7); //false
sout(s6 == s7); //false
String s8 = s5.intern(); //返回值得到的s8使用的常量值中已经存在的“javaEEhadoop”
sout( s3 == s8); //true
}
结论:
-
常量于常量的拼接结果在常量池。并且常量池中不会存在相同内容的常量
-
只要其中有一个是变量,结果就在堆中
-
如果拼接的结果调用intern()方法,返回值就在常量池中
-
面试题注意
-
@Test public void test4{ String s1 = "javaEEhadoop"; String s2 = "javaEE"; String s3 = s2 + "hadoop"; sout(s1 == s3); //false final String s4 = "javaEE"; //s4 常量,存在常量池中 String s5 = s4 + "hadoop"; sout(s1 == s5); //true }
String 常用方法
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bODMnHeY-1678933451147)(C:\Users\ziyi\AppData\Roaming\Typora\typora-user-images\image-20221130164231204.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UPXBaeCE-1678933451148)(C:\Users\ziyi\AppData\Roaming\Typora\typora-user-images\image-20221130164251429.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-PgJKhkvL-1678933451148)(C:\Users\ziyi\AppData\Roaming\Typora\typora-user-images\image-20221130164156308.png)]
String和 char[] / byte[] 之间的转换
- String 与 char[] 之间的转换
String --> char[] : 调用String的toCharArray()
char[] --> String : 调用String的构造器
@Test
public void test1{
String str1 = "abc123";
char[] charArray = str1.toCharArray();
for( int i = 0; i < charArray.lenght; i++){
sout(charArray[i]);
}
char[] arr = new char[]{'h','a','l','o'};
String st2 = new String(arr);
sout(str2);
}
- String与 byte 之间的转换
编码: String --> byte[] : 调用String的getBytes()
解码: byte[] --> String : 调用String的构造器
编码: 字符串 --> 字节(看得懂 —> 看不懂的二进制数据)
解码: 编码的逆过程,字节 --> 字符串 (看不懂的二进制数据 —> 看得懂)
说明: 解码时,要求解码使用的字符集必须与编码时使用的字符集一致,否则出现乱码
@Test
public void test3{
String str1 = "abc123中国";
byte[] bytes = str1.getBytes(); //使用默认的字符集,进行编码(utf-8)
sout(Arrays.toString(bytes));
byte[] gbks = str1.getBytes("gbk"); //使用gbk字符集进行编码
sout(Arrays.toString(gbks));
String str2 = new String(bytes);
sout(str2);
String str3 = new String(gbks);
sout(str3);
String str4 = new String(gbks,"gbk");
sout(str4);
}
String、StringBuffer、StringBuilder
String、StringBuffer、StringBuilder异同
-
String: 不可变的字符串序列: 底层使用char[]存储
-
StringBuffer : 可变的字符序列: 线程安全,效率低 :底层使用char[]存储(synchronized)
-
StringBuilder :可变的字符串序列 : jdk5.0新增的,线程不安全,效率高:底层使用char[]存储
-
源码分析:
String str = new String(); //char[] value = new char[0]; String str1 = new String("abc"); //char[] value = new char[]{'a','b','c'}; //体现为可变,可以追加字符 StringBuffer sb1 = new StringBuffer(); // char[] value = new char[16]; sout(sb1.lenght()); sb1.append('a'); //value[0] = 'a'; sb2.append('b'); // value[1] = 'b'; StringBuffer sb2 = new StringBuffer("abc"); //char[] value = new char["abc".lenght() + 16]; //问题1. sout(sb2); // 3 //问题2. 扩容问题: 如果在添加的数据底层数组盛不下了,那就需要扩容底层数组。默认情况下,扩容数组为原来容量的2倍 + 2, 同时将原来数组中的元素复制到新的数组中。 // 指导意义: 开发中建议使用: StringBuffer(int capacity) 或 StringBuilder(int capacity) 指定容量。(考虑到线程安全问题,在选择使用哪一个)
StringBuffer的常用方法
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-S9FMSP2a-1678933451148)(C:\Users\ziyi\AppData\Roaming\Typora\typora-user-images\image-20221201095539744.png)]
Java中两个Date的使用
/*
java.util.Date 类 java中的Date
|----java.sql.Date类 数据库中的Date
1.两个构造器的使用
> 构造器一: Date() : 创建一个对应当前时间的Date对象
> 构造器二: 创建指定毫秒数的Date对象
2.两个方法的使用
> toString() : 显示当前的年、月、日、时、分、秒
> getTime() : 获取当前Date对象对应的毫秒数(时间戳)
3. java.sql.Date对应着数据库中的日期类型的变量
> 如何实例化
> 如何将java.util.Date对象转换为java.sql.Date对象
*/
@Test
public void test1(){
//构造器一: Date() : 创建一个对应当前时间的Date对象
Date date1 = new Date();
System.out.println(date1);//Thu Dec 01 10:57:38 CST 2022
System.out.println(date1.getTime());//1669863458264
//构造器二: 创建指定毫秒数的Date对象
Date date2 = new Date(343453453453L);
System.out.println(date2);//Wed Nov 19 11:44:13 CST 1980
//创建java.sql.Date对象
java.sql.Date date3 = new java.sql.Date(2324234234234L);
System.out.println(date3); //2043-08-27
//如何将java.util.Date对象向转换为java.sql.Date对象
//情况一:
Date date4 = new java.sql.Date(23323423423L);
java.sql.Date date5 = (java.sql.Date)date4;
System.out.println(date5);
//情况二:
Date date6 = new Date();
java.sql.Date date7 = new java.sql.Date(date6.getTime());
}
SimpleDateFormat的使用
- 两个操作
- 格式化: 日期 —> 字符串
- 解析: 格式化的逆过程, 字符串 —> 日期
- SimpleDateFormat的实例化
@Test
public void test5() throws ParseException {
//实例化SimpleDateFormat : 使用构造器
SimpleDateFormat sdf = new SimpleDateFormat();
//格式化: 日期 ----> 字符串
Date date = new Date();
System.out.println(date);
String format = sdf.format(date);
System.out.println(format);
//解析: 格式化的逆过程, 字符串 ---> 日期
String str = "22-12-01 下午16:01";
Date date1 = sdf.parse(str);
System.out.println(date1);
//********按照指定的方式 --格式化-- 和 --解析--: 调用带参构造器 ********
// SimpleDateFormat sdf1 =new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
SimpleDateFormat sdf1 =new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
//格式化
String format1 = sdf1.format(date);
System.out.println(format1);//2022-12-01 04:07:50
//解析
Date date2 = sdf1.parse("2022-12-01 04:07:50");
System.out.println(date2);
}
练习题
@Test
public void test6() throws ParseException {
String start = "1990-01-01";
String end = "2022-12-01";
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
Date sdate = sdf.parse(start);
Date edate = sdf.parse(end);
int countDay =(int) ((edate.getTime() - sdate.getTime()) / (1000 * 60 * 60 * 24 ) + 1);
System.out.println(countDay % 5);
if (countDay % 5 == 1 || countDay % 5 ==2 || countDay % 5 == 3){
System.out.println("今天在捕鱼!!!");
}else{
System.out.println("今天在晒网!!!");
}
}
Calendar的使用
- 实例化(Calendar为接口,无法实例化,只能创建子类对象或者调用静态方法)
- 方式一: 创建其子类(GregorianCalendar) 的对象
- 方式二: 调用其静态方法 getInstance()
- 常用方法
- get()
- set()
- add()
- getTime()
- setTime()
public void test7(){
//调用静态方法
Calendar calendar = Calendar.getInstance();
//2.常用方法
//get() 获取当前时间是该月份的第几天
int days = calendar.get(Calendar.DAY_OF_MONTH);
//set() 设置
calendar.set(Calendar.DAY_OF_MONTH,22);
System.out.println(calendar.get(Calendar.DAY_OF_MONTH));
//add() 加三天
calendar.add(Calendar.DAY_OF_MONTH,3);
//getTime() : 日历类 --> Daet
Date date = calendar.getTime();
System.out.println(date);
//setTime() : Date --> 日历类
Date date1 = new Date();
calendar.setTime(date1);
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-g8XMWPPt-1678933451148)(C:\Users\ziyi\AppData\Roaming\Typora\typora-user-images\image-20221202095237972.png)]
以下是JDK8的时间API
LocalDate/Time/DateTime
/*
* LocalDate / LocalTime / LocalDateTime
* 说明: LocalDateTime相较于另外两个用频率更高
* */
@Test
public void test8(){
//now(): 获取当前的日期、时间、日期 + 时间
LocalDate localDate = LocalDate.now();
LocalTime localTime = LocalTime.now();
LocalDateTime localDateTime = LocalDateTime.now();
System.out.println(localDate); //2022-12-02
System.out.println(localTime); //10:01:58.881
System.out.println(localDateTime); //2022-12-02T10:01:58.881
//of(): 设置指定的年、月、日、时、分、秒。没有偏移量
LocalDateTime of = LocalDateTime.of(2022,12,02,10,7,33);
System.out.println(of);
//getXxx() : 获取相关属性
System.out.println(localDateTime.getDayOfMonth());
System.out.println(localDateTime.getDayOfWeek());
System.out.println(localDateTime.getMinute());
//体现不可变性
//withXxx() : 设置属性
LocalDate localDate1 = localDate.withDayOfMonth(12);
System.out.println(localDate);
System.out.println(localDate1);
}
Instant类的使用
/*
* Instant 的使用
* 类似于Java.util.Date
* */
@Test
public void test9(){
//now() : 获取本初子午线对应的标准时间
Instant now = Instant.now();
System.out.println(now);
//添加时间偏移量
OffsetDateTime offsetDateTime = now.atOffset(ZoneOffset.ofHours(8));
System.out.println(offsetDateTime);
//toEpochMilli() : 获取自1970年1月1日0时0分0秒 (UTC)开始的毫秒数 ---> Date类的getTime()
long milli = now.toEpochMilli();
System.out.println(milli);
//ofEpochMilli() : 通过给定的毫秒数,获取Instant实例 ---> Date(long millis)
Instant instant = Instant.ofEpochMilli(2345365756L);
System.out.println(instant);
}
DateTimeFormatter使用
/*
*DateTimeFormatter: 格式化或解析日期、时间
* 类似于SimpleDateFormat
* */
@Test
public void test10(){
//方式一:
// 预定义的标准格式 。如: ISO_LOCAL_DATE_TIME;ISO_LOCAL_DATE;ISO_LOCALTIME
DateTimeFormatter formatter = DateTimeFormatter.ISO_LOCAL_DATE_TIME;
//格式化: 日期 --> 字符串
LocalDateTime localDateTime = LocalDateTime.now();
String str1 = formatter.format(localDateTime);
System.out.println(localDateTime);
System.out.println(str1);
//解析: 字符串 --> 日期
TemporalAccessor parse = formatter.parse("2022-12-02T10:35:28.149");
System.out.println(parse);
//方式二
//本地化相关的格式: 如: ofLocalizedDateTime()
//FormatStyle.LONG / FormatStyle.MEDIUM / FormatStyle.SHORT : 适用于LocalDateTime
DateTimeFormatter formatter1 = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.LONG);
DateTimeFormatter formatter2 = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.SHORT);
String str2 = formatter1.format(localDateTime);
String str3 = formatter2.format(localDateTime);
System.out.println(str2); //2022年12月2日 上午10时47分06秒
System.out.println(str3); //22-12-2 上午10:47
//本地化相关的格式:如: ofLocalizedDate()
DateTimeFormatter formatter3 = DateTimeFormatter.ofLocalizedDate(FormatStyle.MEDIUM);
//格式化
String str4 = formatter3.format(LocalDate.now());
System.out.println(str4); //2022-12-2
//方式三 (重点)
// 自定义格式: 如:ofPattern("yyyy-MM-dd hh:mm:ss")
DateTimeFormatter formatter4 = DateTimeFormatter.ofPattern("yyyy-MM-dd");
String format = formatter4.format(LocalDateTime.now());
System.out.println(LocalDateTime.now()); //2022-12-02T10:56:44.534
System.out.println(format); //2022-12-02
}
Java比较器概述
java比较器说明
- 说明: Java中的对象,正常情况,只能继续比较: == 或 != ,不能使用 > huo < 的,但是在开发场景中,我们需要对多个对象进行排序。也就是需要比较对象的大小。
- 具体实现: 使用两个接口中的任意一个: Comparable 或 Comparator
Comparable自然排序举例
@Test
public void test11(){
/*
* Comparable 接口的使用举例: 自然排序
* 1.像String、包装类等实现类Comparable接口,重写了compareTo(obj) 方法,给出了比较两个对象大小的方法
* 2.像String、包装类重写了compareTo() 方法后,进行了从小到大的排序
* 3.重写了compareTo(obj) 的规则:
* 如果当前对象this大于形参对象obj,则返回正整数,
* 如果当前对象this小于形参对象obj,则返回负整数,
* 如果当前对象this等于形参对象obj,则返回0,
* 4.对于自定义类来说,如果需要排序,就需要自定义类实现Comparable接口,重写ComparaTo(obj)方法
* 在compareTo(obj)方法中指明如何排序
*
* */
String[] arr = new String[]{"aa","gg","bb","zz"};
Arrays.sort(arr);
System.out.println(Arrays.toString(arr)); //[aa, bb, gg, zz]
}
@Test
public void test12(){
Person[] people = new Person[4];
people[0] = new Person("Tandy",18);
people[1] = new Person("Candy",16);
people[2] = new Person("Zack",22);
people[3] = new Person("Honghong",32);
Arrays.sort(people);
System.out.println(Arrays.toString(people));
}
}
class Person implements Comparable {
String name;
int age;
public Person(){
}
public Person(String name, int age){
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public int compareTo(Object o) {
if (o instanceof Person){
Person person = (Person) o;
if (this.age > person.age){
return 1;
}else if (this.age < person.age){
return -1;
}else {
return 0;
}
}
throw new RuntimeException("年龄数据非法");
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
Comparator定制排序
java.util.Comparator
-
背景
-
当元素的类型没有实现java.lang.Comparable接口而又不方便修改代码或者实现了java.lang.Comparable接口的排序规则不适合当前排序,那么可以考虑使用 Comparator 的对象来排序
-
重写compare(Object 01, Object o2) 方法, 比较o1 和 o2的大小:
如果方法返回正整数,则表示o1 大于 o2
如果返回0,表示相等
返回负整数,表示o1 小于 o2
-
String 中比较例子
@Test
public void test13() {
String[] arr = new String[]{"AA", "DD", "TT", "LL", "ZZ"};
Arrays.sort(arr, new Comparator() {
@Override
public int compare(Object o1, Object o2) {
if (o1 instanceof String && o2 instanceof String){
String s1 = (String) o1;
String s2 = (String) o2;
return -s1.compareTo(s2);
}
throw new RuntimeException("数据类型不一致!");
}
});
System.out.println(Arrays.toString(arr));
}
对象排序例子
@Test
public void test14(){
Person[] arr = new Person[5];
arr[0] = new Person("Tandy",18);
arr[1] = new Person("Candy",22);
arr[2] = new Person("Zack",22);
arr[3] = new Person("Zack",40);
arr[4] = new Person("Honghong",22);
Arrays.sort(arr,new Comparator() {
@Override
public int compare(Object o1, Object o2) {
if (o1 instanceof Person && o2 instanceof Person){
Person p1 = (Person) o1;
Person p2 = (Person) o2;
if (p1.getName().equals(p2.getName())){
return -Double.compare(p1.getAge(),p2.getAge());
}else {
return p1.getName().compareTo(p2.getName());
}
}
throw new RuntimeException("输入数据类型不一致");
}
});
System.out.println(Arrays.toString(arr));
}
Comparable接口与Comparator的使用对比
- Comparable接口的方式一旦指定,保证Comparable接口实现类的对象在任何位置都可以比较大小
- Comparator 接口属于临时性的比较
其他常用类
System类
@Test
public void test15(){
System.out.println("java.home:" + System.getProperty("java.home"));// java.home:D:\ProgramFiles\Java\jdk1.8.0_171\jre
System.out.println("os的name:" + System.getProperty("os.name"));//os的name:Windows 10
System.out.println("os 的 version:" + System.getProperty("os.version"));//os 的 version:10.0
System.out.println("user_name:" + System.getProperty("user.name"));//user_name:ziyi
System.out.println("user_home:" + System.getProperty("user.home"));//user_home:C:\Users\ziyi
System.out.println("user_dir:" + System.getProperty("user.dir"));//user_dir:E:\workspace\ideaProject\backProject\JavaStudy
}
Math类
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-m9q1n5NW-1678933451149)(C:\Users\ziyi\AppData\Roaming\Typora\typora-user-images\image-20221202152754087.png)]
BigInteger类 与 BigDecimal类
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6y1nobML-1678933451149)(C:\Users\ziyi\AppData\Roaming\Typora\typora-user-images\image-20221202152901055.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GM4Q6uAI-1678933451149)(C:\Users\ziyi\AppData\Roaming\Typora\typora-user-images\image-20221202152951103.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-sIj1dz2j-1678933451149)(C:\Users\ziyi\AppData\Roaming\Typora\typora-user-images\image-20221202153058751.png)]
枚举类
一、枚举类的使用
- 枚举类的理解: 类的对象只有有限个,确定的。我们称此类为枚举类
- 当需要定义一组常量时,强烈建议使用枚举类
- 如果枚举类中只有一个对象,则可以作为单例模式的实现方式
二、如何定义枚举类
方式一: jdk5.0以前,自定义枚举类
//--------方式一
@Test
public void test16(){
Season spring = Season.SPRING;
System.out.println(spring);
}
class Season{
//1.声明Season对象的属性: private final修饰
private final String seasonName;
private final String seasonDesc;
//2.私有化类的构造器,并给对象属性赋值
private Season(String seasonName, String seasonDesc){
this.seasonName = seasonName;
this.seasonDesc = seasonDesc;
}
//3.提供当前枚举类的多个对象:public static final的
public static final Season SPRING = new Season("春天","春乱花开");
public static final Season SUMMER = new Season("夏天","夏日炎炎");
public static final Season AUTUMN = new Season("秋天","秋高气爽");
public static final Season WINTER = new Season("冬天","冰天雪地");
//4.其他诉求 1:获取枚举类对象属性
public String getSeasonName(){
return seasonName;
}
public String getSeasonDesc(){
return seasonDesc;
}
@Override
public String toString() {
return "Season{" +
"seasonName='" + seasonName + '\'' +
", seasonDesc='" + seasonDesc + '\'' +
'}';
}
}
方式二: jdk5.0以后, 可以使用enum关键字定义枚举类
//使用enum关键字定义枚举类
//说明: 定义的枚举类默认继承java.lang.Enum类,
enum Season1{
//1.提供当前枚举类的多个对象,多个对象之间 使用 ”,“ 隔开,末尾用 ”;“ 隔开
SPRING("春天","春乱花开"),
SUMMER("夏天","夏日炎炎"),
AUTUMN("秋天","秋高气爽"),
WINTER("冬天","冰天雪地");
//1.声明Season对象的属性: private final修饰
private final String seasonName;
private final String seasonDesc;
//2.私有化类的构造器,并给对象属性赋值
private Season1(String seasonName, String seasonDesc){
this.seasonName = seasonName;
this.seasonDesc = seasonDesc;
}
//4.其他诉求 1:获取枚举类对象属性
public String getSeasonName(){
return seasonName;
}
public String getSeasonDesc(){
return seasonDesc;
}
@Override
public String toString() {
return "Season{" +
"seasonName='" + seasonName + '\'' +
", seasonDesc='" + seasonDesc + '\'' +
'}';
}
三、Enum中的主要方法
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3wDuMA5Z-1678933451149)(C:\Users\ziyi\AppData\Roaming\Typora\typora-user-images\image-20221205113523553.png)]
四、使用enum关键字定义的枚举类实现接口的情况
- 情况一:实现接口 ,在enum类 中实现抽象类
- 情况二:让枚举类的对象分别实现接口中的抽象方法
//使用enum关键字定义枚举类
//说明: 定义的枚举类默认继承java.lang.Enum类,
interface Info{
void show();
}
enum Season1 implements Info{
//1.提供当前枚举类的多个对象,多个对象之间 使用 ”,“ 隔开,末尾用 ”;“ 隔开
SPRING("春天","春乱花开"){
@Override
public void show() {
System.out.println("春天");
}
},
SUMMER("夏天","夏日炎炎"){
@Override
public void show() {
System.out.println("夏天");
}
},
AUTUMN("秋天","秋高气爽"){
@Override
public void show() {
System.out.println("秋天");
}
},
WINTER("冬天","冰天雪地") {
@Override
public void show() {
System.out.println("冬天");
}
};
//1.声明Season对象的属性: private final修饰
private final String seasonName;
private final String seasonDesc;
//2.私有化类的构造器,并给对象属性赋值
private Season1(String seasonName, String seasonDesc){
this.seasonName = seasonName;
this.seasonDesc = seasonDesc;
}
//4.其他诉求 1:获取枚举类对象属性
public String getSeasonName(){
return seasonName;
}
public String getSeasonDesc(){
return seasonDesc;
}
// @Override
// public void show() {
// System.out.println("这是一个季节");
// }
}
//尚硅谷P502
注解的使用
- 如何自定义注解: 参照SuperessWarnings定义
- 注解声明:@interface
- 内部定义成员, 通常使用value表示
- 可以指定成员的默认值,使用default定义
- 如果自定义注解没有成员,表明是一个表示作用
如果注解有成员,在使用注解时,需要指明成员的值。
自定义注解必须配上注解的信息处理流程(使用反射)才有意义
自定义注解通常都会指明两个元注解: Retention、Target
- jkd 提供的4中元注解
元注解: 对现有的注解进行解释说明的注解
- Retention:指定所修饰的Annotation的生命周期: SOURCE/ CLASS(默认行为) / RUMTIME , 只有声明为RUNTIME声明周期的注解,才能通过反射获取。
- Target: 用于指定被修饰的Annotation能修饰那些结构
- Documented:表示所修饰的注解在被javadoc解析时,保留下来
- Inherited:被他修饰的 Annotation 将具有继承性
- jdk8新特性:
集合
集合的概述
- 集合、数组都是对多个数据进行存储操作的结构,简称Java容器
说明: 此时的存储,最主指的是内存层面的存储,不涉及到持久化的存储
-
数组在存储多个数据方面的特点
- 一旦初始化以后,其长度就确定了
- 数组一旦定义好,其元素的类型就确定了,也只能操作指定类型的数据了。
- 比如: String[] arr; int[] arr1;
-
数组在存储多个数据方面的特点:
- 一旦初始化以后,其长度不可修改
- 数组中提供的方法非常有限,添加、删除、插入等操作等,非常不方便,同时效率不高
- 获取数组中实际元素的个数的需求,数组没有现成的属性或方法可用
- 数组存储数据的特点: 有序、可重复。对于无序、不可重复的需求不满足
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fIOuwktv-1678933451150)(C:\Users\ziyi\AppData\Roaming\Typora\typora-user-images\image-20221205152706377.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6aZqbELE-1678933451150)(C:\Users\ziyi\AppData\Roaming\Typora\typora-user-images\image-20221205152738334.png)]
集合框架
|----collection接口:单列集合,用来存储一个一个的对象
| ----list接口:存储有序的、可重复的数据。-->“动态”数组
|----Arraylist. LinkedList. Vector
|----Set接口:存储无序的、不可重复的数据-->高中讲的集合”
|----HashSet. LinkedHashset. Treeset
|----Map接口:双列集合,用来存储一对(key - value)一对的数据―-->高中函数: y = f(x)
|----HashMap、LinkedHashMap.TreeMap.Hashtable、Properties
Collection接口
结论: 涉及集合的比较时,需要重写equals方法。
package Collection;
import com.sun.org.apache.xpath.internal.operations.String;
import org.junit.jupiter.api.Test;
import java.lang.String.*;
import java.util.*;
public class CollectionTest {
public static void main(String[] args) {
Collection coll = new ArrayList();
List coll2 = new ArrayList();
Set coll3 = new LinkedHashSet();
Map coll4 = new HashMap();
coll4.put("001","kelsdlfj");
coll4.put("002","kelsdlfj");
coll4.put("003","kelsdlfj");
coll.add("aa");
coll.add("bb");
coll.add(123);
coll.add(new Date());
System.out.println(coll.size());
Collection coll1 = new ArrayList();
coll1.add(456);
coll1.add("cc");
coll.addAll(coll1);
System.out.println(coll.size());
System.out.println(coll);
// clear():清空集合元素
// isEmpty(): 判断当前集合是否为空
System.out.println(coll.isEmpty());
}
@Test
public void test(){
Collection coll = new ArrayList();
coll.add(123);
coll.add(456);
coll.add(new Person("jerry",20));
coll.add(new java.lang.String("tom"));
coll.add(false);
//1.contains(Object obj) : 判断当前集合中是否包含obj
//当判断时会调用obj对象所在类的equals()
boolean contains = coll.contains(123);
System.out.println(contains);
System.out.println(coll.contains(new java.lang.String("tom")));
//2.containsAll(Collection coll) : 判断形参coll1中的所有元素是否都存在于当前集合中
Collection coll1 = Arrays.asList(123,456);
System.out.println(coll.containsAll(coll1));
}
@Test
public void test1(){
//3.remove(Object obj) : 从当前集合中移除Obj元素
Collection coll = new ArrayList();
coll.add(123);
coll.add(456);
coll.add(new Person("jerry",20));
coll.add(new java.lang.String("tom"));
coll.add(false);
coll.remove(123);
System.out.println(coll);
coll.remove(new Person("jerry",20));
System.out.println(coll);
//4.removeAll(Collection coll1) : 从当前集合中移除coll1中所有的元素
Collection coll1 = Arrays.asList(123,456);
coll.removeAll(coll1);
System.out.println(coll);
}
@Test
public void test2(){
Collection coll = new ArrayList();
coll.add(123);
coll.add(456);
coll.add(new Person("jerry",20));
coll.add(new java.lang.String("tom"));
coll.add(false);
//5.retianAll(Collection coll1) : 交集: 获取当前集合和coll1集合的交集,并返回
// Collection coll1 = new ArrayList();
// coll.retainAll(coll1);
// System.out.println(coll);
//6.equals(Object obj) : 当前集合与形参集合的元素是否相等
//7.hashCode(): 返回当前对象的哈希值
System.out.println(coll.hashCode());
//8.集合 ---> 数组 :toArray()
Object[] arr = coll.toArray();
for (int i = 0; i < arr.length; i++) {
System.out.println(arr[i]);
}
//拓展: 数组 ---> 集合
List<java.lang.String> list = Arrays.asList(new java.lang.String[]{"aa", "bb", "cc"});
System.out.println(list);
//特别注意一下两种写法
List arr1 = Arrays.asList(new int[]{123, 345});
System.out.println(arr1.size());//1
List arr2 = Arrays.asList(new Integer[]{123, 345});
System.out.println(arr2.size());//2
}
}
Iterator迭代器
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-aSO9xhv6-1678933451150)(C:\Users\ziyi\AppData\Roaming\Typora\typora-user-images\image-20221206142206573.png)]
/*
* 集合元素的遍历操作,使用迭代器Iterator接口
* 1.内部方法: hasNext() 和 next()
* 2.集合对象每次调用iterator()方法都得到一个全新的迭代器对象默认游标都在结合的第一个元素之前
* 3.内部定义了remove(),可以在遍历的时候,删除集合中的元素。此方法不同于集合直接调用remove()
*/
@Test
public void test3(){
Collection coll = new ArrayList();
coll.add(123);
coll.add(456);
coll.add(new Person("jerry",20));
coll.add(new java.lang.String("tom"));
coll.add(false);
Iterator iterator = coll.iterator();
System.out.println(iterator.next());
//正确方式
while (iterator.hasNext()){
System.out.println(iterator.next());
}
//错误方式一:
Iterator iterator1 = coll.iterator();
while (iterator1.next() != null){
System.out.println(iterator1.next());//跳着输出
}
//错误方式二:
//集合对象每次调用iterator()方法都得到一个全新的迭代器对象,默认游标都在集合的第一个
while (coll.iterator().hasNext()){
System.out.println(coll.iterator().next());
}
}
//注意: 没有调用next()方法走之前或者上一次调用next()方法会后已经调用了remove方法,再调用remove都会报IllegalStateExcetpion
增强foreach循环
@Test
public void test4(){
Collection coll = new ArrayList();
coll.add(123);
coll.add(456);
coll.add(new Person("jerry",20));
coll.add(new java.lang.String("tom"));
coll.add(false);
for (Object obj : coll) {
System.out.println(obj);
}
int[] arr = new int[]{1,2,3,4,5};
for (int i: arr) {
System.out.println(i);
}
}
练习题细节:
//jdk5.0 新增了foreach循环,用于遍历集合、数组
@Test
public void test5(){
java.lang.String[] str = new java.lang.String[]{"aa","bb","cc"};
//情况一:
// for (int i = 0; i < str.length; i++) {
// str[i] = "ff";
// }
// for (int i = 0; i < str.length; i++) {
// System.out.println(str[i]); //结果ff ff ff
// }
//情况二:增强for循环
for (java.lang.String s : str) {
s = "mm";
}
for (int i = 0; i < str.length; i++) {
System.out.println(str[i]); //结果 aa bb cc
}
}
List接口
框架结构
*1.List接口框架
* |----Collection接口:单列集合,用来存储一个一个的对象
* |----list接口:存储有序的、可重复的数据。
* |----ArrayList:作为list接口的主要实现类: 线程不安全,效率高: 底层使用Object[] elementData数组
* |----LinkedList:对于频繁的插入、删除操作,使用此类效率比ArrayList高:底层使用链表
* |----Vector:作为list接口的古老实现类:线程安全,效率低;底层使用Object[] elementData数组
源码分析
/*
*1.List接口框架
* |----Collection接口:单列集合,用来存储一个一个的对象
* |----list接口:存储有序的、可重复的数据。
* |----ArrayList:作为list接口的主要实现类: 线程不安全,效率高: 底层使用Object[] elementData数组
* |----LinkedList:对于频繁的插入、删除操作,使用此类效率比ArrayList高:底层使用链表
* |----Vector:作为list接口的古老实现类:线程安全,效率低;底层使用Object[] elementData数组
*
*2.ArrayList源码分析:
* 2.1 jdk1.7情况下:
* ArrayList list = new ArrayList(); //底层创建长度是10的Object[]数组elementData
* list.add(123);//elementData[0] = new Integer(123);
* ...
* list.add(11);//如果此次的添加导致底层elementData数容量不够,则扩容
* 默认情况下,扩容为原来容量的1.5倍,同时需要将原有的数组中的数据复制到新数组中。
*
* 结论:建议开发这种使用带参构造器: ArrayList list = new ArrayList(int capacity)
*
* 2.2 jdk1.8
* ArrayList list = new ArrayList();//底层Object[] elementData初始化为{},并没有创建长度为10的数组
* list.add(112);//第一次用add()时,底层才创建长度10的数组,并将数据添加到elementData[0]中
* ...
* 后续的添加和扩容操作与jdk1.7无异
* 2.3 小结: jdk7中的ArrayList的对象的创建类似于单例的饿汉式,而jdk8中的ArrayList的对象
* 的创建类似于单例的懒汉式,延迟了数组的创建,节省内存。
*
* 3.LinkedList源码分析
* LinkedList list = new LinkedList(); 内部声明了Node类型的first和last属性,默认值为null
* list.add(123); //将123封装到Node中,创建了Node对象
* private static class Node<E>{
* E item;
* Node<E> next;
* Node<E> prev;
*
* Node(Node<E> prev, E element, Node<E> next){
* this.item = element;
* this.next = next;
* this.prev = prev;
* }
* }
*
*
*
* 面试题:ArrayList、LinkedList、Vector三者异同?
* 同:三个类都实现了List接口,存储数据的特点相同:存储有序的、可重复的数据
* 不同: 见上
* */
@Test
public void test(){
}
常用方法
public class ListTest {
public static void main(String[] args) {
/*
* list常用方法
* void add(int index ,Object ele): 在index位置插入ele元素
* boolean addAll(int index, Collection eles): 从index位置开始讲eles中的所有元素添加进来
* Object get(int index): 获取指定index位置的元素
* int indexOf(Object obj) : 返回obj 在集合中首次出现的位置
* int LastIndexOf(Object obj): 返回object在当前集合中末次出现的位置
* Object remove(int index): 移除指定的index位置的元素,并返回此元素
* Object set(int index , Object ele): 设置指定的index位置的元素为ele
* List subList(int fromIndex, int toIndex): 返回formindex到toindex 位置的子集合*/
/*遍历ArrayList的三种方法
* 方式一:
* Iterator迭代器方式
* 方式二:
* 增强for循环
* 方式三:
* for循环*/
/*总结常用方法
* 增:add(object obj)
* 删:remove(int index) / remove(object obj)
* 改:set(int index, object ele)
* 查:get(int index)
* 插:add(int index, object)
* 长度:size()
* 编列:iterator 增强for循环 普通for循环
*注意区分remove方法
* remove(2) 与 remove(new integer(2))
* 第一个是按索引删除,第二个是删除2这个数据对象 */
ArrayList list = new ArrayList();
list.add(123);
list.add(456);
list.add("aa");
list.add(new Person("tom",12));
list.add(456);
System.out.println(list);
list.add(1,"xuec");
System.out.println(list);
ArrayList list1 = new ArrayList();
list1.add(123);
list1.add(456);
list1.add("aa");
list.addAll(list1);
System.out.println(list);
System.out.println(list.get(2));
System.out.println(list.indexOf("aa"));
System.out.println(list.lastIndexOf("xuec"));
System.out.println(list.remove(2));
System.out.println(list.subList(0, 2));
}
Set接口
package Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
/*set接口的框架结构
* |----Collection接口: 单列接口,用来存储一个一个的对象
* |-----Set接口:存储无序的、不可重复的数据 --》 高中讲的“集合”
* |-----HashSet:作为set接口的主要实现类: 线程不安全; 可以存储null值
* |-----LinkedHasSet:作为hashset的子类,遍历其内部数据时,可以按照添加的顺序遍历
* 对于频繁遍历操作,LinkedHashSet效率高于HashSet
* |-----TreeSet:可以按照添加的对象指定的属性,进行排序*/
/*1. Set接口中没有额外定义新的方法,使用的都是Collection中声明过的方法
* 2. 要求: 想Set 中添加的数据,其所在类一定要重写hashCode()和equals()
* 要求: 重写的hashCode() 和 equals() 尽可能保持一致性: 相等的对象必须具有相等的散列码
* 重写两个方法: 对象中用作equals()方法比较的Field, 都应该用来计算hashCode*/
/*一:Set: 存储无序的、不可重复的数据
* 以HashSet为例说明:
* 1. 无序性: 不等于随机性。存储的数据在底层数组中并非按照数组索引的顺序添加,而是根据数据的哈希值
* 2. 不可重复性: 保证添加的元素按照equals()判断时,不能反回true,即:相同的元素只能添加一个
*
* 二:添加元素的过程: 以HashSet为例:
* 我们想Hashset中添加元素a,首先调用元素a所在类的hashCode()方法,计算元素a 的哈希值,
* 此哈希值接着通过某种算法计算出在Hashset底层数组中存放的位置(即:所以位置),判断
* 数组此位置上是否已经有元素:
* 如果此位置上没有其他元素,则元素a添加成功 ---> 情况1
* 如果此位置上有其他元素b(或者以链表形式存在多个元素),则比较元素a与元素b 的hash值:
* 如果hash值不相同,则元素a添加成功 --->情况2
* 如果hash值相同,进而需要调用元素a所在类的equals()方法:
* equals()返回true,元素a 添加失败
* equals()返回false,则元素添加成功 --->情况3
*
* 对于添加成功的情况2、 3而言: 元素a 与已经存在指定索引位置上数据以链表的方式存储
* jdk7 : 元素a放到数组中,指定原来元素
* jdk8 : 原来的元素在数组中, 指向元素a
* 总结: 七上八下
*
* HashSet 底层: 数组+ 链表的结构*/
public class SetTest {
public static void main(String[] args) {
Set set = new HashSet();
set.add(456);
set.add(123);
set.add("aa");
set.add("cc");
set.add(new Person("tom",12));
set.add(129);
System.out.println("直接打印:"+set);
Iterator iterator = set.iterator();
while (iterator.hasNext()){
System.out.println(iterator.next());
}
}
}
Map接口
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jlmHBRDY-1678933451151)(C:\Users\ziyi\AppData\Roaming\Typora\typora-user-images\image-20221209095159632.png)]
HashMap
package Collection;
/* |----Map:双列数据,存储key-value 对的数据 --- 类似于高中的函数: y= f(x)
* |----HashMap: 作为Map的主要实现类: 线程不安全的,效率高; 存储null的key和value
* |----LinkedHashMap: 保证在遍历map元素时,可以按照添加的顺序实现遍历
* 原因: 在原有的HashMap底层结构基础上,添加一个指针,指向前一个和后一个元素
* 对于频繁的遍历操作, 此类执行的效率高于HashMap
* |----TreeMap:保证按照添加的key-value对进行排序,实现排序遍历。此时考虑key 的自然排序或者定制排序
* 底层使用红黑树
* |----Hashtable: 作为古老的实现类;线程安全的,效率低;不能存储null的key和value
* |----Properties: 常用来处理配置文件。 key和value都是String类型
*
* HashMap的底层: 数组+链表 (jdk7及以前)
* 数组+链表+红黑树(jdk8)
*
* 面试题:
* 1. HashMap的底层实现原理?
* 2. HashMap 和 Hashtable 的异同?
* 3. CurrentHashMap 与 Hashtable 的异同?
*
* 二、Map结构的理解
* Map中的key: 无序的、不可重复的,使用set存储所有的key ---> 所在的类要重写equals()和hashCode()
* Map中的value: 无序的、可以重复的, 使用Collection存储所有的value ---> value所在的类要重写equals()
* 一个键值对: key-value 构成了一个Entry对象
* Map中的entry: 无序的、不可重复, 使用set存储所有的entry
*
* 三、 HashMap的底层实现原理? 以jdk7 为例说明:
* HashMap map = new HashMap()
* 在实例化以后,底层创建了长度16的一维数组entry[] table
* ...可能已经执行了很多次put...
* map.put(key1,value1):
* 首先,调用key1所在类的hashCode()计算key1的哈希值,此哈希值经过某种算法计算后,得到在entry数组中的存放位置,
* 如果此位置上的数据为空, 此时的key1-value1添加成功。 情况1
* 如果此位置上的数据不为空, (意味着此位置上存在一个或者多个数据(以链表的形式存在)), 比较key1 和已经存在的
* 一个或者多个数据的哈希值:
* 如果key1 的哈希值与已经存在的数据的哈希值都不相同, 此时key1-value1 添加成功 -----情况2
* 如果key1的哈希值和已经存在的某个数据(key2-value2) 的哈希值相同,继续比较:调用key1所在类的equals(key2):
* 如果equals()返回false:此时key1-value1 添加成功 ----情况3
* 如果equals()返回true: 使用value1 替换value2
*
* 补充: 关于情况2、3: 此时key1-value1 和原来的数据以链表的方式存储
* 在不断添加过程中,会涉及到扩容问题,默认的扩容方式 : 扩容为原来容量的2倍, 并将原有的数据复制过来。
*
* jdk8 相较于jdk7 在底层实现方面的不同
* 1. new HashMap(): 底层没有创建一个长度为16的数组
* 2.jdk8 底层的数组是: Node[] ,而非Entry[]
* 3. 首次调用put方法是,底层创建长度为16的数组
* 4.jdk7 底层结构只有: 数组+链表 jdk8 : 数组+ 链表+ 红黑树
* 当数组的某一个索引位置上的元素以链表形式存在的个数 > 8 且当前数组长度 > 64
* 此时此索引位置上的所有数据改为使用红黑树存储。
*
*
* DEFAULT_INITIAL_CAPACITY : HashMap的默认容量,16
* DEFAULT_LOAD_FACTOR: HashMap的默认加载因子:0.75
* threshold:扩容的临界值,=容量*填充因子:16 * 0.75 =>12
* TREEIFY_THRESHOLD: Bucket中链表长度大于该默认值,转化为红黑树:8
* MIN_TREEIFY_CAPACITY:桶中的Node被树化时最小的hash表容量:64
*
* 四、LinkedHashMap的底层实现原理(了解)
*
* 五、 Map中定义的方法:
* 添加、 删除、 修改操作
* Object put(Object key ,Object value) : 将指定key-value添加到(或修改—)当前map对象中
* void putALL(Map m): 将m中的所有key-value对存放到当前map中
* Object remove(Object key): 移除指定key-value对,并返回value
* void clear(): 清空当前map中的所有数据
* 元素查询操作:
* Object get(Object key): 获取指定key对应的value
* boolean containsKey(Object key) : 是否包含指定的key
* Boolean containsValue(Object value): 是否包含指定的value
* int size(): 返回map 中的key-value对的个数
* boolean isEmpty(): 判断当前map是否为空
* Boolean equals(Object obj) : 判断当前map和参数对象obj是否相等
*
* 总结常用方法:
* 添加:put(Object key ,Object value)
* 删除:remove(Object key)
* 修改:put(Object key, Object value)
* 查询:get(Object key)
* 长度:size()
* 遍历:keySet() / values () / entrySet()
* */
import java.util.*;
public class MapTest {
public static void main(String[] args) {
Map map = new HashMap();
map.put("aa",123);
map.put(45,123);
map.put("bb",56);
map.put("aa",87); //此处的"aa" 修改了第一个“aa”中的值
//遍历map的所有的key
Set set = map.keySet();
Iterator iterator = set.iterator();
while (iterator.hasNext()){
System.out.println(iterator.next());
}
System.out.println();
//遍历map的所有的value
Collection values = map.values();
Iterator iterator1 = values.iterator();
while (iterator1.hasNext()){
System.out.println(iterator1.next());
}
System.out.println();
//遍历map所有的key-value
//方式一: entrySet()
Set entrySet = map.entrySet();
Iterator iterator2 = entrySet.iterator();
while (iterator2.hasNext()){
Object obj = iterator2.next();
//entrySet 集合中的所有元素都是entry
Map.Entry entry = (Map.Entry) obj;
System.out.println(entry.getKey()+"--->"+entry.getValue());
}
//方式二
Set keySet = map.keySet();
Iterator iterator3 = keySet.iterator();
while (iterator3.hasNext()){
Object key = iterator3.next();
Object value = map.get(key);
System.out.println(key+"-==="+ value);
}
}
}
TreeMap
package Collection;
import org.junit.jupiter.api.Test;
import java.util.*;
public class TreeMapTest {
/* 向TreeMap中添加key-value, 要求key必须是由同一个创建的对象
* 因为要按照key进行排序: 自然排序、 定制排序
* */
//一、自然排序
public static void main(String[] args) {
TreeMap map = new TreeMap();
Person p1 = new Person("Tom",23);
Person p2 = new Person("Jerry",32);
Person p3 = new Person("Jack",20);
Person p4 = new Person("Rose",18);
map.put(p1,89);
map.put(p2,98);
map.put(p3,78);
map.put(p4,100);
Set entrySet = map.entrySet();
Iterator iterator = entrySet.iterator();
while (iterator.hasNext()){
Object next = iterator.next();
Map.Entry entry = (Map.Entry)next;
System.out.println(entry.getKey()+"==="+ entry.getValue());
}
}
//二、定制排序
@Test
public void test(){
TreeMap map = new TreeMap(new Comparator() {
@Override
public int compare(Object o1, Object o2) {
if (o1 instanceof Person && o2 instanceof Person){
Person p1 = (Person) o1;
Person p2 = (Person) o2;
// return p1.getAge() - p2.getAge();
return Integer.compare(p1.getAge(),p2.getAge());
}
throw new RuntimeException("数据异常!!");
}
});
Person p1 = new Person("Tom",23);
Person p2 = new Person("Jerry",32);
Person p3 = new Person("Jack",20);
Person p4 = new Person("Rose",18);
map.put(p1,89);
map.put(p2,98);
map.put(p3,78);
map.put(p4,100);
Set entrySet = map.entrySet();
Iterator iterator = entrySet.iterator();
while (iterator.hasNext()){
Object next = iterator.next();
Map.Entry entry = (Map.Entry)next;
System.out.println(entry.getKey()+"==="+ entry.getValue());
}
}
}
Properties
package Collection;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.Properties;
public class PropertiesTest {
//Properties: 常用来处理配置文件, key和value都是String类型
public static void main(String[] args) throws IOException {
Properties pros = new Properties();
FileInputStream fis = new FileInputStream("spring");
pros.load(fis); //加载流对应的文件
String name = pros.getProperty("name");
String age = pros.getProperty("age");
System.out.println("name = " + name + "password = "+ age);
}
}
Collections工具类
package Collection;
/*
* reverse(list): 反转list 中元素的顺序
* shuffle(list): 对list集合元素进行随机排序
* sort(list): 根据元素的自然顺序对指定list集合元素按照升序排序
* sort(list, Comparator): 根据指定的 Comparator 产生的顺序对list 集合中元素进行排序
* swap(list, int , int): 将指定list集合中的 i 处的元素和 j 处元素进行交换
*
* Object max(Collection) : 根据元素的自然排序, 返回给定集合中的最大元素
* Object max(Collection, Comparator): 根据Comparator指定的顺序,返回给定集合中的最大
* Object min(Collection)
* Object min(Collection)
* int frequency(Colletion, Object): 返回指定集合中指定元素的出现次数
* void copy(list dest, list src): 将src 中的内容复制到dest中
*
* */
import org.junit.jupiter.api.Test;
import org.omg.CORBA.IRObject;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.sql.SQLOutput;
import java.util.*;
public class CollectionsTest {
@Test
public void test(){
ArrayList list = new ArrayList();
List list1 = new LinkedList();
List list2 = new Vector();
list.add(123);
list.add(43);
list.add(14);
list.add(-12);
list.add(0);
//报异常: IndexOutOfBoundsException("Source does not fit in dest")
//List dest = new ArrayList();
//Collections.copy(dest,list);
//正确写法:
List dest = Arrays.asList(new Object[list.size()]);
System.out.println(dest.size());
Collections.copy(dest,list);
System.out.println(dest);
/*Collections 类中提供了多个sybchronizedXXX()方法,该方法可以使将指定集合包装成
* 线程同步的集合,从而可以解决多线程并发访问集合时的线程安全问题*/
//返回的list即成为线程安全的list1
List list4 = Collections.synchronizedList(list);
}
@Test
public void test2(){
Collection coll = new ArrayList();
//add(Obj e) :将元素e添加到结合coll
coll.add("Aa");
coll.add("ba");
coll.add(213);//自动装箱
coll.add(new Date());
//size(): 获取添加的元素的个数
System.out.println(coll.size());
//addAll(Collection coll1): 将coll1集合中的元素添加到当前集合中
Collection coll1 = new ArrayList();
coll1.add("Dd");
coll1.addAll(coll);
//clear(): 情况集合元素
coll.clear();
//isEmpty(): 判断当即集合是否为空
System.out.println(coll.isEmpty());
}
泛型(Generic)
为什么要有泛型
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bUtHi6CS-1678933451151)(C:\Users\ziyi\AppData\Roaming\Typora\typora-user-images\image-20221209155541156.png)]
泛型概念
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XQxC0Iqv-1678933451151)(C:\Users\ziyi\AppData\Roaming\Typora\typora-user-images\image-20221209155716959.png)]
泛型使用
package Generic;
import org.junit.jupiter.api.Test;
import java.util.*;
/* 泛型的使用
* 1.jdk 5.0 新增特性
*
* 2.在集合中使用泛型
* 总结:
* ①集合接口或集合类在jdk5.0 时都修改为带泛型的结构
* ②在实例化结合类时候, 可以指明具体的泛型类型
* ③在指明完以后,在集合类或接口中凡是定义类或接口时,内部结构(比如: 方法、构造器、属性等)
* 使用到类的泛型的位置,都是指定为实例化的泛型类型。比如: add(E e) --->实例化后: add(Integer e)
* ④注意点: 泛型的类型必须是类,不是基本数据类型。需要使用到基本数据类的位置,去拿包装类
* ⑤ 如果实例化时, 没有指明泛型的类型。默认类型为java.lang.Object类型
* 3.*/
public class GenericTest {
//在集合中使用泛型之前的情况
public static void main(String[] args) {
ArrayList list = new ArrayList();
//需求: 存放学生成绩
list.add(89);
list.add(90);
list.add(60);
list.add(59);
//问题一: 类型不安全
list.add("Tom");
for(Object score : list){
// 问题二: 强转时, 可能出现ClassCastException
int stuScore = (Integer) score;
System.out.println(stuScore);
}
}
@Test
public void test2(){
ArrayList<Integer> list = new ArrayList<Integer>();
list.add(89);
list.add(79);
list.add(69);
list.add(59);
//编译时,就会进行类型检查,保证数据的安全
//list.add("tom");
Iterator<Integer> iterator = list.iterator();
while (iterator.hasNext()){
System.out.println(iterator.next());
}
}
//在集合中使用泛型的情况: 以HashMap为例
@Test
public void test3(){
HashMap<String, Integer> map = new HashMap<String, Integer>();
map.put("tom",89);
map.put("jerry",89);
map.put("jack",89);
// 遍历
Set<Map.Entry<String, Integer>> entry = map.entrySet();
Iterator<Map.Entry<String, Integer>> iterator = entry.iterator();
while (iterator.hasNext()){
Map.Entry<String, Integer> next = iterator.next();
String key = next.getKey();
Integer value = next.getValue();
System.out.println(key+ "----"+ value);
}
}
}
IO流
IO流原理及流的分类
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-YrWgES2D-1678933451151)(C:\Users\ziyi\AppData\Roaming\Typora\typora-user-images\image-20221222100415956.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Hyty6p7K-1678933451152)(C:\Users\ziyi\AppData\Roaming\Typora\typora-user-images\image-20221222100359141.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-lN8MThmF-1678933451152)(C:\Users\ziyi\AppData\Roaming\Typora\typora-user-images\image-20221222100617571.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-C9V6TgpL-1678933451152)(C:\Users\ziyi\AppData\Roaming\Typora\typora-user-images\image-20221222100810260.png)]
FileReaderWriter
package IO;
import org.junit.Test;
import thread.WindowsTest;
import java.io.*;
/**
* @author xuec
* @date 2022/12/22
**/
/*
* 一、流的分类
* 1.操作数据单位: 字节流、 字符流
* 2.数据的流向: 输入流、 输出流
* 3.流的角色: 节点流、 处理流
*
* 二、流的体系结构
* 抽象基类 节点流(或文件流) 缓冲流(处理流的一种)
* InputStream FileInputStream BufferedInputStream
* OutputStream FileOutputStream BufferedOutputStream
* Reader FileReader BufferedReader
* Writer FileWriter BufferedWriter
* */
/* 说明点:
* 1. read()的理解:返回读入的一个字符。如果达到文件末尾,返回-1
* 2. 异常的处理: 为保证流资源一定可以执行关闭操作。需要使用try-catch-finally处理
* 3. 读入的文件一定要存在,否则就会报FileNotFoundException
* */
public class FileReaderWriter {
//调read()的无参构造器
@Test
public void testFileReader() {
FileReader fileReader = null;
try {
File file = new File("E:\\workspace\\ideaProject\\backProject\\JavaStudy\\src\\main\\java\\IO\\hello.txt");
fileReader = new FileReader(file);
int data;
while ((data = fileReader.read()) != -1){
System.out.print((char) data);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (fileReader != null)
fileReader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
//对read()操作升级: 使用read的重载方法
@Test
public void FileReader1() throws IOException {
File file = new File("hello.txt");
FileReader fileReader = new FileReader(file);
char[] buf = new char[5];
int len;
while ((len = fileReader.read(buf)) != -1){
// 方式一
//错误的写法
// for (int i = 0; i < buf.length; i++) {
// System.out.print(buf[i]);
//正确写法
// for (int i = 0; i < len; i++) {
// System.out.println(buf[i]);
// }
// 方式二:
// 错误写法
// String str = new String(buf);
// System.out.println(str);
// 正确写法
String str = new String(buf,0,len);
System.out.println(str);
}
fileReader.close();
}
/*
从内存中写出数据到硬盘的文件里
说明:
1. 输出操作,对应的File可以不存在。并不会报错
如果不存在会自动创建文件
如果存在:
使用构造器是:FileWriter(file,false) / FileWriter(file): 对原有文件的覆盖
使用构造器是:FileWriter(file,true):不覆盖原内容,在原内容上追加
*/
@Test
public void FileWriter() {
FileWriter fileWriter = null;
try {
File file = new File("hello1.txt");
fileWriter = new FileWriter(file,false);
fileWriter.write("i have a dream!");
fileWriter.write("you need to have a dream!");
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (fileWriter != null)
fileWriter.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
@Test
public void FileReaderFileWriter(){
//1.
FileReader fr = null;
FileWriter fw = null;
try {
//1.创建File类的对象,指明读入和写出的文件
File srcFile = new File("hello1.txt");
File destFile = new File("hello2.txt");
//2.创建输入输出流的对象
fr = new FileReader(srcFile);
fw = new FileWriter(destFile);
//3.数据的读入和写出操作
char[] cbuf = new char[5];
int len; //记录每次读入到cbuf数组中的字符个数
while ((len = fr.read(cbuf)) != -1){
//每次写出len个字符
fw.write(cbuf,0,len);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (fw != null)
fw.close();
} catch (IOException e) {
e.printStackTrace();
}
try {
if (fr != null)
fr.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
FileInputOutputStream
package IO;
import org.junit.Test;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
/**
* @author xuec
* @date 2022/12/22
**/
/*
* 结论:
* 1.对于文本文件(.txt , .java, .c , .cpp),使用字符流
* 2.对于非文本文件(.jpg , .mp3, .mp4), 使用字节流处理
*
* 注意:文本文件可以用字节流做复制,但是不能再内存层面写出来查看,会出现乱码。
* */
public class FileInputStreamFileOutputStream {
@Test
public void FileInputOutputStream(){
FileInputStream in = null;
FileOutputStream out = null;
try {
File srcFile = new File("hello1.txt");
File destFile = new File("hello2.txt");
in = new FileInputStream(srcFile);
out = new FileOutputStream(destFile);
byte[] buf = new byte[5];
int len;
while ((len = in.read(buf)) != -1){
out.write(buf,0,len);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (in != null)
in.close();
} catch (IOException e) {
e.printStackTrace();
}
try {
if (out != null)
out.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
缓冲流的使用
BufferedInputOutputStream
package IO;
import org.junit.Test;
import java.io.*;
/**
* @author xuec
* @date 2022/12/22
**/
/*
* 处理流之一: 缓冲流的使用
*
* 1.缓冲流
* BufferedInputStream
* BufferedOutputStream
* BufferedReader
* BufferedWriter
*
* 2.作用: 提高流的读取、写入速度
* */
public class Buffered {
@Test
public void BufferedStream(){
BufferedInputStream bis = null;
BufferedOutputStream bos = null;
try {
File srcFile = new File("E:\\workspace\\image\\meizhi.png");
File destFile = new File("E:\\workspace\\image\\nihao.png");
FileInputStream fis = new FileInputStream(srcFile);
FileOutputStream fos = new FileOutputStream(destFile);
bis = new BufferedInputStream(fis);
bos = new BufferedOutputStream(fos);
byte[] buffer = new byte[10];
int len;
while ((len = bis.read(buffer)) != -1){
bos.write(buffer,0,len);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
//资源关闭
// 要求: 需要先关闭外层流,在关闭内层流
// 说明: 关闭外层流的同时,会自动关闭内层流,内层流关闭可省略
try {
if ( bos != null)
bos.close();
} catch (IOException e) {
e.printStackTrace();
}
try {
if (bis != null)
bis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
BufferedReaderWriter
@Test
public void BufferedReaderWriter(){
BufferedReader br = null;
BufferedWriter bw = null;
try {
br = new BufferedReader(new FileReader(new File("hello.txt")));
bw = new BufferedWriter(new FileWriter(new File("hello4.txt")));
char[] cbuf = new char[1024];
int len;
while ((len = br.read(cbuf)) != -1){
bw.write(cbuf,0,len);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (bw != null)
bw.close();
} catch (IOException e) {
e.printStackTrace();
}
try {
if (br != null)
br.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
转换流、编码
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RHsOkSRf-1678933451152)(C:\Users\ziyi\AppData\Roaming\Typora\typora-user-images\image-20221223092208598.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LuSkSVKH-1678933451153)(C:\Users\ziyi\AppData\Roaming\Typora\typora-user-images\image-20221223094322782.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-j8NG743L-1678933451153)(C:\Users\ziyi\AppData\Roaming\Typora\typora-user-images\image-20221223094538472.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GEQOihyb-1678933451153)(C:\Users\ziyi\AppData\Roaming\Typora\typora-user-images\image-20221223094619887.png)]
package IO;
import org.junit.Test;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
/**
* @author xuec
* @date 2022/12/23
**/
/* 处理流之二: 转换流的使用
* 1.转换流: 属于字符流
* InputStreamReader: 将一个字节的输入流转换为字符的输入流
* OutputStreamWriter: 将一个字符的输出流转换为字节的输出流
*
* 2.作用: 提供字节流与字符流之间的转换
*
* 3. 解码: 字节、字节数组 ---> 字符数组、字符串
* 编码: 字符数组、字符串 ---> 字节、 字节数组
*
* 4.字符集
* ASCII : 美国标准信息交互码。用一个字节的7位可以表示
* ISO8859-1 : 拉丁码表。欧洲码表,用一个字节的8位表示
* GB2312 : 中国的中文编码表。最多两个字节编码所有字符
* GBK : 中国的中文编码表升级,融合率更多的中文文字字符号。最多两个字节编码
* Unicode : 国际标准码,融合了目前人类使用的所有字符。为每一个字符分配唯一的字符码
* UTF-8 : 变长的编码方式,可以1-4个字节来表示一个字符
*
* */
public class testInputStreamReader {
@Test
public void test() {
InputStreamReader isr = null;
try {
FileInputStream fis = new FileInputStream("hello.txt");
//使用系统默认的字符集
// InputStreamReader isr = new InputStreamReader(fis);
//指明字符集,具体用哪个字符集,参照文件存储时使用的字符
isr = new InputStreamReader(fis, "utf-8");
char[] cbuf = new char[1024];
int len;
while ((len = isr.read(cbuf)) != -1){
String s = new String(cbuf, 0, len);
System.out.println(s);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (isr != null)
isr.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
数据流
package IO;
import org.junit.Test;
import java.io.*;
/**
* @author xuec
* @date 2022/12/23
**/
public class OhterStream {
/*数据流的使用
* DataInputStream 和 DataOutputStream
* 作用: 用于读取或写出基本数据类型的变量或字符串
* 注意: 读取不同数据类型的数据时,要和写入文件时的顺序保持一致
* */
@Test
public void test() throws IOException{
DataOutputStream dos = new DataOutputStream(new FileOutputStream("data.txt"));
dos.writeUTF("李刚");
dos.flush();
dos.writeInt(13);
dos.flush();
dos.writeBoolean(true);
dos.flush();
dos.close();
}
@Test
public void test1() throws IOException{
DataInputStream dis = new DataInputStream(new FileInputStream("data.txt"));
String name = dis.readUTF();
int age = dis.readInt();
boolean isMale =dis.readBoolean();
System.out.println("name :" + name);
System.out.println("age :" + age);
System.out.println("isMale :" + isMale);
dis.close();
}
}
对象流、序列化
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6IpXLjEm-1678933451153)(C:\Users\ziyi\AppData\Roaming\Typora\typora-user-images\image-20221223110515779.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-OvK2SBYE-1678933451154)(C:\Users\ziyi\AppData\Roaming\Typora\typora-user-images\image-20221223110814830.png)]
package IO;
import org.junit.Test;
import java.io.*;
/**
* @author xuec
* @date 2022/12/23
*
* 对象流的使用
* 1. ObjectInputStream ObjectOutputStream
* 2. 作用: 用于存储和读取基本数据类型数据 或 对象的处理流。
* 3.想要一个java对象可序列化,需要满足那些要求:
* 3.1.需要 implements Serializble接口
* 3.2 当前类提供一个全局常量:serialVersionUID
* 3.3 需要当前类的所有属性也要可序列化(默认基本数据类型可序列化)
* 补充: ObjectOutputStream 和 ObjectInputStream 不能序列化static和transient修饰的成员
* */
public class ObjectInputOutputStream {
/* 序列化过程: 将内存中的java对象保存到磁盘中或通过网络传输出去
* 使用ObjectOutputStream实现
* */
@Test
public void testObjectOutputStream(){
ObjectOutputStream oos = null;
try {
oos = new ObjectOutputStream(new FileOutputStream("data1.dat"));
oos.writeObject(new String("我是蜡笔小新"));
oos.flush();
oos.writeObject(new Person("子怡",18));
oos.flush();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (oos != null) {
try {
oos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
@Test
public void ObjectInputStream(){
ObjectInputStream ois = null;
try {
ois = new ObjectInputStream(new FileInputStream("data1.dat"));
Object obj = ois.readObject();
String str = (String) obj;
System.out.println(str);
} catch (IOException | ClassNotFoundException e) {
e.printStackTrace();
} finally {
if (ois != null){
try {
ois.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
RandomAccessFile
网络编程
TCP网络编程
练习
package web;
import org.junit.Test;
import java.io.*;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.UnknownHostException;
/**
* @author xuec
* @date 2022/12/26
**/
public class TCPTest {
@Test
public void client() {
Socket socket = null;
OutputStream os = null;
try {
//1.创建Socket对象,指明服务器端的IP和端口号
InetAddress inet = InetAddress.getByName("127.0.0.1");
socket = new Socket(inet, 8089);
//2.获取一个输出流,用于输出数据
os = socket.getOutputStream();
//3.写出数据的操作
os.write("你好,我是客户端mm".getBytes());
} catch (IOException e) {
e.printStackTrace();
} finally {
//4.资源关闭
if (os != null){
try {
os.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (socket != null){
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
@Test
public void server() {
ServerSocket ss = null;
Socket socket = null;
InputStream is = null;
ByteArrayOutputStream baos = null;
try {
//1.创建服务器端的ServerSocket,指明自己的端口号
ss = new ServerSocket(8089);
//2.调用accept()表示接收来自客户端的socket
socket = ss.accept();
//3.获取输入流
is = socket.getInputStream();
//不建议这样写,可能出现乱码
// byte[] buf = new byte[10];
// int len;
// while ((len = is.read(buf)) != null){
// String str = new String(buf, 0, len);
// System.out.println(str);
// }
//4.读取输入流中的数据
baos = new ByteArrayOutputStream();
byte[] buf = new byte[10];
int len;
while ((len = is.read(buf)) != -1){
baos.write(buf,0,len);
}
System.out.println(baos.toString());
} catch (IOException e) {
e.printStackTrace();
} finally {
//5.资源关闭
if (baos != null){
try {
baos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (is != null){
try {
is.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (socket != null){
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (ss != null){
try {
ss.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
练习二
package web;
import com.sun.javafx.sg.prism.web.NGWebView;
import org.junit.Test;
import java.io.*;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
/**
* @author xuec
* @date 2022/12/27
*
* 实现TCP的网络编程
* 例题2:客户端发送文件给服务器,服务端将文件保存到本地
* */
public class TCPTest1 {
@Test
public void client() throws IOException{
InetAddress inet = InetAddress.getByName("127.0.0.1");
Socket socket = new Socket(inet, 8089);
OutputStream os = socket.getOutputStream();
FileInputStream fis = new FileInputStream("尤物.jpg");
BufferedOutputStream bos = new BufferedOutputStream(os);
BufferedInputStream bis = new BufferedInputStream(fis);
byte[] buf = new byte[1024];
int len;
while ((len = bis.read(buf)) != -1){
bos.write(buf,0,len);
}
bis.close();
bos.close();
socket.close();
}
@Test
public void server() throws IOException{
ServerSocket ss = new ServerSocket(8089);
Socket socket = ss.accept();
InputStream is = socket.getInputStream();
FileOutputStream fos = new FileOutputStream("youwu.jpg");
byte[] buf = new byte[10];
int len;
while ((len = is.read(buf)) != -1){
fos.write(buf,0,len);
}
fos.close();
is.close();
socket.close();
ss.close();
}
}
练习三
package web;
import org.junit.Test;
import java.io.*;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
/**
* @author xuec
* @date 2022/12/27
*
* TCP网络编程练习
* 3.客户端发送图片给服务端,服务端保存图片到本地并返回信息给客户端
* 客户端接收服务器返回的信息并打印到控制台
* */
public class TCPTest2 {
@Test
public void client() throws IOException {
Socket socket = new Socket(InetAddress.getByName("127.0.0.1"), 8099);
FileInputStream fis = new FileInputStream("尤物.jpg");
OutputStream os = socket.getOutputStream();
byte[] buf = new byte[1024];
int len;
while ((len = fis.read(buf)) != -1){
os.write(buf,0,len);
}
//注意: 此处需要手动停止Socket输出,不然服务端read方法停不下来
socket.shutdownOutput();
//5.接收来自服务器端的数据,并显示在控制台
InputStream is = socket.getInputStream();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte[] buffer = new byte[20];
int len1;
while ((len1 = is.read(buffer)) != -1){
baos.write(buffer,0,len1);
}
System.out.println(baos);
os.close();
fis.close();
socket.close();
}
@Test
public void server() throws IOException{
ServerSocket ss = new ServerSocket(8099);
Socket socket = ss.accept();
InputStream is = socket.getInputStream();
FileOutputStream fos = new FileOutputStream("youwu2.jpg");
byte[] buf = new byte[10];
int len;
while ((len = is.read(buf)) != -1){
fos.write(buf,0,len);
}
System.out.println("图片传输完成");
OutputStream os = socket.getOutputStream();
os.write("你好,美女,照片已经收到,非常漂亮!".getBytes());
os.close();
fos.close();
is.close();
socket.close();
}
}
UDP网络编程
package web;
import org.junit.Test;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
/**
* @author xuec
* @date 2022/12/27
*
* UDP协议网络编程
* */
public class UDPTest {
//发送端
@Test
public void sender(){
DatagramSocket socket = null;
try {
socket = new DatagramSocket();
String str = "我是UDP方式发送的导弹";
byte[] data = str.getBytes();
InetAddress inet = InetAddress.getLocalHost();
DatagramPacket packet = new DatagramPacket(data,0,data.length,inet,9090);
socket.send(packet);
} catch (IOException e) {
e.printStackTrace();
} finally {
if (socket != null)
socket.close();
}
}
@Test
public void receiver() throws IOException{
DatagramSocket socket = new DatagramSocket(9090);
byte[] buf = new byte[100];
DatagramPacket packet = new DatagramPacket(buf,0,buf.length);
socket.receive(packet);
System.out.println(new String(packet.getData(),0,packet.getLength()));
socket.close();
}
}
URL网络编程
package web;
import org.junit.Test;
import java.net.MalformedURLException;
import java.net.URL;
/**
* @author xuec
* @date 2022/12/26
*
* URL网络编程
* 1.URL: 统一资源定位符, 对应着互联网的某一资源地址
* 2.格式 :
* http://localhost:8080/examples/beauty.jpg?username=Tom
* 协议 主机名 端口号 资源地址 参数列表
* */
public class URLTest {
@Test
public void testURL(){
try {
URL url = new URL("http://localhost:8080/examples/beauty.jpg?username=Tom");
//获取该URL的协议名
System.out.println(url.getProtocol());
//获取该URL的主机名
System.out.println(url.getHost());
//获取该URL的端口号
System.out.println(url.getPort());
//获取URL的文件路径
System.out.println(url.getPath());
//获取该URL的查询名
System.out.println(url.getFile());
System.out.println(url.getQuery());
} catch (MalformedURLException e) {
e.printStackTrace();
}
}
}
反射机制reflection
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zo9iCBFS-1678933451154)(C:\Users\ziyi\AppData\Roaming\Typora\typora-user-images\image-20221227160353149.png)]
使用反射前后的区别
package reflection;
import org.junit.Test;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
/**
* @author xuec
* @date 2022/12/27
**/
public class ReflectionTest {
//反射之前,对于Person操作
@Test
public void test1(){
//1.创建Person类的对象
Person p1 = new Person("Tom", 12);
//2.通过对象,调用其内部的属性、方法
p1.age = 10;
System.out.println(p1.toString());
p1.show();
//在Person类外部,不可以通过Person类的对象调用其内部私有的结构
//比如: name showNation()以及其私有的构造器
}
//有反射之后,对于Person的操作
@Test
public void test2() throws Exception{
Class clazz = Person.class;
//1.通过反射,创建Person类的对象
Constructor cons = clazz.getConstructor(String.class, int.class);
Object obj = cons.newInstance("Tom", 12);
Person p = (Person) obj;
System.out.println(p.toString());
//2.通过反射,调用对象指定的属性、方法
//调属性
Field age = clazz.getDeclaredField("age");
age.set(p,10);
System.out.println(p);
//调用方法
Method show = clazz.getDeclaredMethod("show");
show.invoke(p);
System.out.println("***************************************");
//通过反射,可以调用Person类的私有化结构。构造器、方法、属性
//调用私有的构造器
Constructor cons1 = clazz.getDeclaredConstructor(String.class);
cons1.setAccessible(true);
Person p1 = (Person) cons1.newInstance("Jerry");
System.out.println(p1);
//调用私有的属性
Field name = clazz.getDeclaredField("name");
name.setAccessible(true);
name.set(p1,"HanMeiMei");
System.out.println(p1);
//调用私有的方法
Method showNation = clazz.getDeclaredMethod("showNation", String.class);
showNation.setAccessible(true);
String nation = (String) showNation.invoke(p1,"中国"); //相当于p1.showNation("中国")
System.out.println(nation);
}
// 疑问1: 通过直接new的方式或反射的方式都可以调用公共的结构,开发中选哪个
// 建议: 直接new方式
// 什么时候会使用: 反射的方式。 反射的特征 : 动态性
// 疑问2: 反射机制与面向对象中的封装性是不是矛盾? 如何看待
// 不矛盾
/*
* 关于java.lang.Class类的理解
* 1. 类的加载过程:
* 程序经过java.exe命令过后,会生成一个或多个字节码we年(.class结尾)
* 接着我们使用java.exe命令对某一个字节码we年进行解释运行。相当于将某
* 个字节码文件加载到内存,此过程成为类的加载。 加载到内存中的类,我们
* 就称为运行时类,此运行时类,就作为Class的一个实例
* 2.换句话说,Class的实例对应着一个运行时类
* 3.加载到内存的运行时类,会缓存一段时间,在此时间内,可以通过不同的方式
* 来获取此运行时类。
*
* 注意: 获取的三种方式需要掌握
* */
@Test
public void test3() throws ClassNotFoundException {
//1.方式一: 调用运行时类的属性: .class
Class clazz1 = Person.class;
System.out.println(clazz1);
//2.方式二:通过运行时类的对象
Person p1 = new Person();
Class clazz2 = p1.getClass();
System.out.println(clazz2);
//3.方式三:调用Class的静态方法: forName(String classPath)
Class clazz3 = Class.forName("reflection.Person");
System.out.println(clazz3);
}
}
使用ClassLoader加载配置文件
@Test
public void test2() throws IOException {
Properties pros = new Properties();
//此时的文件默认在当前module
//读取配置文件的方式一:
// FileInputStream fis = new FileInputStream("jdbc.properties");
// pros.load(fis);
//读取配置文件的方式二: 使用ClassLoader
//配置文件默认识别为: 当前module的src下
ClassLoader classLoader = ClassLoaderTest.class.getClassLoader();
InputStream is = classLoader.getResourceAsStream("jdbc.properties");
pros.load(is);
String user = pros.getProperty("user");
String password = pros.getProperty("password");
System.out.println("use:" + user + "password" + password);
}
通过反射创建运行时类的对象
package reflection;
import org.junit.Test;
import sun.security.jca.GetInstance;
import java.util.Random;
/**
* @author xuec
* @date 2022/12/28
*
* 通过反射创建运行时类的对象
* */
public class NewInstanceTest {
@Test
public void test1() throws InstantiationException, IllegalAccessException {
Class<Person> clazz = Person.class;
/*
* newInstance() : 调用此方法,创建对应的运行时类的对象
* 内部调用了运行时类的空参构造器
* 要想此方法正常的创建运行时类的对象,要去
* 1.运行时类必须提供空参构造器
* 2.空参构造器的访问权限得够。通常设置为 public
*
* 在javabean中要求提供一个public的空参构造器。原因:
* 1.便于通过反射,创建运行时类的对象
* 2.便于子类继承此运行时类时,默认调用super()时,保证父类有次构造器
* */
Person p = clazz.newInstance();
System.out.println(p);
}
//体会反射的动态性
@Test
public void test2(){
int num = new Random().nextInt(3);
String classpath = "";
switch (num){
case 0:
classpath = "java.util.Date";
break;
case 1:
classpath = "java.lang.Object";
break;
case 2:
classpath = "reflection.Person";
break;
}
Object obj = null;
try {
obj = getInstance(classpath);
} catch (Exception e) {
e.printStackTrace();
}
System.out.println(obj);
}
public Object getInstance(String classPath) throws Exception{
Class clazz = Class.forName(classPath);
return clazz.newInstance();
}
@Test
public void test3(){
}
}
获取运行时类的完整结构
获取属性结构
package reflection;
import org.junit.Test;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
/**
* @author xuec
* @date 2022/12/28
**/
public class FieldTest {
@Test
public void test1(){
Class<Person> clazz = Person.class;
//获取属性结构
//getFields():获取当前运行时类及其父类中声明为public访问权限的属性
Field[] fields = clazz.getFields();
for (Field f : fields) {
System.out.println(f);
}
System.out.println();
//getDeclaredFields(): 获取当前运行时类中声明的所有属性。(不包含父类中的)
Field[] declaredFields = clazz.getDeclaredFields();
for (Field f : declaredFields) {
System.out.println(f);
}
}
//权限修饰符 数据类型 变量名
@Test
public void test2(){
Class<Person> clazz = Person.class;
Field[] declaredFields = clazz.getDeclaredFields();
for (Field f : declaredFields) {
//1.权限修饰符
int modifiers = f.getModifiers();
System.out.println(Modifier.toString(modifiers) + "\t");
//2.数据类型
Class<?> type = f.getType();
System.out.println(type.getName() + "\t");
//3.变量名
String fName = f.getName();
System.out.println(fName);
}
}
}
获取方法结构
package reflection;
import org.junit.Test;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
/**
* @author xuec
* @date 2022/12/28
*
* 获取运行时类的方法结构
**/
public class MethodTest {
@Test
public void test1(){
Class<Person> clazz = Person.class;
//getMethods():获取当前运行时类及其父类的所有声明为public的方法
Method[] methods = clazz.getMethods();
for (Method method : methods) {
System.out.println(method);
}
System.out.println();
//getDeclaredMethods():获取当前运行时类中声明的所有方法。不包含父类
Method[] declaredMethods = clazz.getDeclaredMethods();
for (Method declaredMethod : declaredMethods) {
System.out.println(declaredMethod);
}
}
/*
* 权限修饰符 返回值类型 方法名(参数类型1 形参名1,...) throws XxxException*/
@Test
public void test2(){
Class<Person> clazz = Person.class;
Method[] m = clazz.getDeclaredMethods();
for (Method method : m) {
//1.获取方法声明的注解
Annotation[] anos = method.getAnnotations();
for (Annotation ano : anos) {
System.out.println(ano);
}
//2.权限修饰符
System.out.println(Modifier.toString(method.getModifiers()) + "\t");
//3.返回值类型
System.out.println(method.getReturnType().getName() + "\t");
//4.方法名
System.out.println(method.getName());
//5.形参列表
//6.抛出异常类
}
}
}
其他结构
package reflection;
import org.junit.Test;
import java.lang.reflect.Constructor;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
/**
* @author xuec
* @date 2022/12/28
*
* 获取构造器
* */
public class OtherTest {
@Test
public void test1(){
Class<Person> clazz = Person.class;
Constructor<?>[] constructors = clazz.getConstructors();
for (Constructor<?> c : constructors) {
System.out.println(c);
}
System.out.println();
//获取当前运行时类中声明的所有构造器
Constructor<?>[] declaredConstructors = clazz.getDeclaredConstructors();
for (Constructor<?> c : declaredConstructors) {
System.out.println(c);
}
}
//获取运行时类的父类
@Test
public void test2(){
Class<Person> clazz = Person.class;
Type genericSuperclass = clazz.getGenericSuperclass();
System.out.println(genericSuperclass);
}
//获取运行时类的带泛型的父类的泛型
@Test
public void test3(){
Class<Person> clazz = Person.class;
Type genericSuperclass = clazz.getGenericSuperclass();
ParameterizedType paramType = (ParameterizedType) genericSuperclass;
//获取泛型类型
Type[] actualTypeArguments = paramType.getActualTypeArguments();
for (Type actualTypeArgument : actualTypeArguments) {
System.out.println(actualTypeArgument);
}
}
}
调用运行时类获取指定的属性、方法、构造器
获取指定属性
@Test
public void test4() throws Exception{
Class<Person> clazz = Person.class;
//创建运行时类对象
Person p = clazz.newInstance();
//1.获取运行时类中指定变量名的属性
Field name = clazz.getDeclaredField("name");
//2.保证当前属性是可访问的
name.setAccessible(true);
//3.获取、设置指定对象的此属性值
name.set(p,"Tom");
System.out.println(name.get(p));
}
获取指定方法
//获取运行时类指定方法
@Test
public void test5() throws Exception{
Class<Person> clazz = Person.class;
Person p = clazz.newInstance();
//1.获取某个指定的方法
//getDeclaredMethod(): 参数1:指明获取的方法名称 参数2:指明获取的方法的形参列表
Method show = clazz.getDeclaredMethod("showNation", String.class);
//2.保证当前方法时可访问的
show.setAccessible(true);
//3.调用方法的invoke(): 参数1:方法的调用者 参数2:给方法形参赋值的实参
//invoke() 的返回值即为对应类中调用的方法的返回值
Object returnValue = show.invoke(p, "CHN");
Method showDesc = clazz.getDeclaredMethod("showDesc");
showDesc.setAccessible(true);
Object invoke = showDesc.invoke(Person.class);
//调用静态方法可以写null,静态属性时候也是一样
Object invoke1 = showDesc.invoke(null);
System.out.println(invoke);
}
获取构造器
@Test
public void test6() throws Exception{
Class<Person> clazz = Person.class;
Constructor<Person> con = clazz.getDeclaredConstructor(String.class);
con.setAccessible(true);
Person p = con.newInstance("tom");
System.out.println(p);
}
JDK8新特性
Lambda表达式
package JDK8新特性;
import org.junit.Test;
import java.util.Comparator;
import java.util.function.Consumer;
/**
* @author xuec
* @date 2022/12/29
*
* Lamdba表达式的使用
* 1.举例:(o1,o2) -> Integer.compare(o1,o2);
* 2.格式:
* ->: lambda操作符 或 箭头操作符
* ->左边: lamdba形参列表(就是接口中的抽象方法的形参列表)
* ->右边:lamdba体 (其实就是重写的抽象方法的方法体)
*
* 3. lamdba表达式的使用:(分为6种情况介绍)
*
* 总结:
* -> 左边:lambda形参列表的参数类型可以省略(类型推断),如果lambda形参列表只有一个参数,其()可以省略
* -> 右边:lambda体应该使用一对{}包裹,如果lambda体只有一条执行语句(可能是return语句),可以省略这对{}和return关键字
* 4.lambda表达式的本质:作为接口的实例
**/
public class LamdbaTest {
//语法格式一: 无参,无返回值
@Test
public void test1(){
Runnable r1 = new Runnable() {
@Override
public void run() {
System.out.println("我爱北京天安门");
}
};
r1.run();
//lambda方式
Runnable r2 = () -> {
System.out.println("我爱北京天安门");
};
r2.run();
}
//语法格式二: lambda需要一个参数,但是没有返回值
@Test
public void test2(){
Consumer<String> con =new Consumer<String>() {
@Override
public void accept(String s) {
System.out.println(s);
}
};
con.accept("欢迎!!!");
Consumer<String> con1 = (String s) ->{
System.out.println(s);
};
con1.accept("lambda方式欢迎!!!");
}
//语法格式三:数据类型可以省略,因为可以由编译器推断得出(类型推断)
@Test
public void test3(){
Consumer<String> con1 = (String s) ->{
System.out.println(s);
};
con1.accept("lambda方式欢迎!!!");
//此处省略了String
Consumer<String> con2 = (s) ->{
System.out.println(s);
};
con2.accept("类型推断!!!");
}
//语法格式四:lambda若只需要一个参数时,参数的小括号可以省略
@Test
public void test4(){
Consumer<String> con1 = (s) ->{
System.out.println(s);
};
con1.accept("ajajjajajajaj");
Consumer<String> con2 = s ->{
System.out.println(s);
};
con2.accept("ajajjajajajaj");
}
//语法格式五:lambda需要两个或以上的参数,多条执行语句,并且可以有返回值
@Test
public void test5() {
Comparator<Integer> com1 = new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
System.out.println(o1);
System.out.println(o2);
return o1.compareTo(o2);
}
};
System.out.println(com1.compare(12, 31));
//lambda
Comparator<Integer> com2 = (o1,o2) ->{
System.out.println(o1);
System.out.println(o2);
return o1.compareTo(o2);
};
System.out.println(com2.compare(32, 21));
}
//语法格式六:当lambda体只有一条语句时,return与大括号若有,都可以省略
@Test
public void test6(){
Comparator<Integer> com2 = (o1,o2) ->{
return o1.compareTo(o2);
};
System.out.println(com2.compare(12, 21));
//lambda
Comparator<Integer> com3 = (o1,o2) -> o1.compareTo(o2);
System.out.println(com3.compare(21, 12));
}
}
函数式接口
如果一个接口中只声明了一个抽象方法,则成为函数式接口
@FunctionInterface 校验该接口是不是函数式接口
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-AEaXTtkN-1678933451154)(C:\Users\ziyi\AppData\Roaming\Typora\typora-user-images\image-20221229145310809.png)]
举例
package JDK8新特性;
import org.junit.Test;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.function.Consumer;
import java.util.function.Predicate;
/**
* @author xuec
* @date 2022/12/29
*
* java 内置的4大核心函数式接口
*
* 消费型接口 Comsumer<T> void accept(T t)
* 提供型接口 Supplier<T> T get()
* 函数型接口 Function<T,R> R apply(T t)
* 断定型接口 Predicate<T> boolean test(T t)
*
* */
public class LambdaTest2 {
@Test
public void test1(){
happyTime(500, new Consumer<Double>() {
@Override
public void accept(Double aDouble) {
System.out.println("买水,价格为"+ aDouble);
}
});
System.out.println("--------------------------");
happyTime(400,money -> System.out.println("买水,价格为"+ money));
}
public void happyTime(double money, Consumer<Double> con){
con.accept(money);
}
@Test
public void test2(){
List<String> list = Arrays.asList("北京","南京","天津","普京");
List<String> listStrs = filterString(list, new Predicate<String>() {
@Override
public boolean test(String s) {
return s.contains("京");
}
});
System.out.println(listStrs);
List<String> listStrs1 = filterString(list,s -> s.contains("京"));
System.out.println(listStrs1);
}
//根据给定的规则,过滤集合中的字符串。此规则由Predicate的方法决定
public List<String> filterString(List<String> list, Predicate<String> pre){
ArrayList<String> filterList = new ArrayList<>();
for (String s : list) {
if (pre.test(s)){
filterList.add(s);
}
}
return filterList;
}
}
StreamAPI
JDK9新特性
模块化系统
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-z0qNi28T-1678933451154)(C:\Users\ziyi\AppData\Roaming\Typora\typora-user-images\image-20230103153016289.png)]
Java的JEPL工具:jshell
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-QBvWBcjV-1678933451155)(C:\Users\ziyi\AppData\Roaming\Typora\typora-user-images\image-20230103154057476.png)]
接口的私有方法
砖石操作符的语法升级
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-iXxm6gKo-1678933451155)(C:\Users\ziyi\AppData\Roaming\Typora\typora-user-images\image-20230103154854216.png)]
语法改进: try语句
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GAPkxIy7-1678933451155)(C:\Users\ziyi\AppData\Roaming\Typora\typora-user-images\image-20230103155436089.png)]
String存储结构变更
tAccessible(true);
Person p = con.newInstance(“tom”);
System.out.println§;
}
# JDK8新特性
## Lambda表达式
```java
package JDK8新特性;
import org.junit.Test;
import java.util.Comparator;
import java.util.function.Consumer;
/**
* @author xuec
* @date 2022/12/29
*
* Lamdba表达式的使用
* 1.举例:(o1,o2) -> Integer.compare(o1,o2);
* 2.格式:
* ->: lambda操作符 或 箭头操作符
* ->左边: lamdba形参列表(就是接口中的抽象方法的形参列表)
* ->右边:lamdba体 (其实就是重写的抽象方法的方法体)
*
* 3. lamdba表达式的使用:(分为6种情况介绍)
*
* 总结:
* -> 左边:lambda形参列表的参数类型可以省略(类型推断),如果lambda形参列表只有一个参数,其()可以省略
* -> 右边:lambda体应该使用一对{}包裹,如果lambda体只有一条执行语句(可能是return语句),可以省略这对{}和return关键字
* 4.lambda表达式的本质:作为接口的实例
**/
public class LamdbaTest {
//语法格式一: 无参,无返回值
@Test
public void test1(){
Runnable r1 = new Runnable() {
@Override
public void run() {
System.out.println("我爱北京天安门");
}
};
r1.run();
//lambda方式
Runnable r2 = () -> {
System.out.println("我爱北京天安门");
};
r2.run();
}
//语法格式二: lambda需要一个参数,但是没有返回值
@Test
public void test2(){
Consumer<String> con =new Consumer<String>() {
@Override
public void accept(String s) {
System.out.println(s);
}
};
con.accept("欢迎!!!");
Consumer<String> con1 = (String s) ->{
System.out.println(s);
};
con1.accept("lambda方式欢迎!!!");
}
//语法格式三:数据类型可以省略,因为可以由编译器推断得出(类型推断)
@Test
public void test3(){
Consumer<String> con1 = (String s) ->{
System.out.println(s);
};
con1.accept("lambda方式欢迎!!!");
//此处省略了String
Consumer<String> con2 = (s) ->{
System.out.println(s);
};
con2.accept("类型推断!!!");
}
//语法格式四:lambda若只需要一个参数时,参数的小括号可以省略
@Test
public void test4(){
Consumer<String> con1 = (s) ->{
System.out.println(s);
};
con1.accept("ajajjajajajaj");
Consumer<String> con2 = s ->{
System.out.println(s);
};
con2.accept("ajajjajajajaj");
}
//语法格式五:lambda需要两个或以上的参数,多条执行语句,并且可以有返回值
@Test
public void test5() {
Comparator<Integer> com1 = new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
System.out.println(o1);
System.out.println(o2);
return o1.compareTo(o2);
}
};
System.out.println(com1.compare(12, 31));
//lambda
Comparator<Integer> com2 = (o1,o2) ->{
System.out.println(o1);
System.out.println(o2);
return o1.compareTo(o2);
};
System.out.println(com2.compare(32, 21));
}
//语法格式六:当lambda体只有一条语句时,return与大括号若有,都可以省略
@Test
public void test6(){
Comparator<Integer> com2 = (o1,o2) ->{
return o1.compareTo(o2);
};
System.out.println(com2.compare(12, 21));
//lambda
Comparator<Integer> com3 = (o1,o2) -> o1.compareTo(o2);
System.out.println(com3.compare(21, 12));
}
}
函数式接口
如果一个接口中只声明了一个抽象方法,则成为函数式接口
@FunctionInterface 校验该接口是不是函数式接口
[外链图片转存中…(img-AEaXTtkN-1678933451154)]
举例
package JDK8新特性;
import org.junit.Test;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.function.Consumer;
import java.util.function.Predicate;
/**
* @author xuec
* @date 2022/12/29
*
* java 内置的4大核心函数式接口
*
* 消费型接口 Comsumer<T> void accept(T t)
* 提供型接口 Supplier<T> T get()
* 函数型接口 Function<T,R> R apply(T t)
* 断定型接口 Predicate<T> boolean test(T t)
*
* */
public class LambdaTest2 {
@Test
public void test1(){
happyTime(500, new Consumer<Double>() {
@Override
public void accept(Double aDouble) {
System.out.println("买水,价格为"+ aDouble);
}
});
System.out.println("--------------------------");
happyTime(400,money -> System.out.println("买水,价格为"+ money));
}
public void happyTime(double money, Consumer<Double> con){
con.accept(money);
}
@Test
public void test2(){
List<String> list = Arrays.asList("北京","南京","天津","普京");
List<String> listStrs = filterString(list, new Predicate<String>() {
@Override
public boolean test(String s) {
return s.contains("京");
}
});
System.out.println(listStrs);
List<String> listStrs1 = filterString(list,s -> s.contains("京"));
System.out.println(listStrs1);
}
//根据给定的规则,过滤集合中的字符串。此规则由Predicate的方法决定
public List<String> filterString(List<String> list, Predicate<String> pre){
ArrayList<String> filterList = new ArrayList<>();
for (String s : list) {
if (pre.test(s)){
filterList.add(s);
}
}
return filterList;
}
}
StreamAPI
JDK9新特性
模块化系统
[外链图片转存中…(img-z0qNi28T-1678933451154)]
Java的JEPL工具:jshell
[外链图片转存中…(img-QBvWBcjV-1678933451155)]
接口的私有方法
砖石操作符的语法升级
[外链图片转存中…(img-iXxm6gKo-1678933451155)]
语法改进: try语句
[外链图片转存中…(img-GAPkxIy7-1678933451155)]
String存储结构变更
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ZUDftTxb-1678933451155)(C:\Users\ziyi\AppData\Roaming\Typora\typora-user-images\image-20230103155956000.png)]