类变量和类方法 类变量 也叫静态变量/静态属性,用static
修饰,可以被本类的所有对象实例共享。任何对象去访问它时访问的都是同一个变量
语法:访问修饰符 static 变量类型 变量名
访问方法:类名.类变量名
、对象名.类变量名
,推荐使用:类名.类变量名
类变量是随着类的加载而创建,所以即使没有创建对象实例也可以访问。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 package com.xzt._static;public class VisitStatic { public static void main (String[] args) { String name = A.name; System.out.println(name); A a = new A (); System.out.println(a.name); } } class A { public static String name = "xzt" ; }
注意事项
当需要让某个类变量都共享一个变量时,可以使用类变量(静态变量)
类变量时所有对象共享的,而实例变量时每个对象独享的
加上static
成为类变量或静态变量,否则成为实例变量/普通变量
类方法 也叫静态方法,用static
修饰
语法:访问修饰符 static 返回类型 方法名(参数列表) {}
访问方法:类名.类方法名
、对象名.类方法名
,推荐使用:类名.类方法名
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 package com.xzt._static;public class StaticMethod { public static void main (String[] args) { Stu tom = new Stu ("tom" ); tom.payFee(100 ); Stu jack = new Stu ("jack" ); jack.payFee(200 ); Stu.showFee(); } } class Stu { public String name; private static double fee = 0 ; public Stu (String name) { this .name = name; } public static void payFee (double fee) { Stu.fee += fee; } public static void showFee () { System.out.println("总学费有" + Stu.fee); } }
使用场景: 当方法中不涉及任何和对象相关的成员,则可以将方法设计成静态方法,提高开发效率,例如工具类:Math 类
注意事项
类方法和普通方法都是随着类的加载而加载,将结构信息存储在方法区
类方法可以通过类名调用,普通方法不能通过类名调用。
main
方法语法:public static void main(String[] args) {}
main
是java虚拟机调用,所以类方法需要是public
java虚拟机在执行main
方法时不需要创建对象,所以需要是static
该方法接收String
类型的数组参数,该数组中保存执行java命令时传递给所运行的类的参数
java 执行的程序 参数1 参数2 参数3
,参数组成String[] args
注意事项
在main
方法中可以直接调用main
方法所在类的静态方法或静态属性。
但是不能直接访问该类中的非静态成员,必须创建该类的一个实例对象后,才能通过这个对象去访问类中的静态成员
代码块 代码块又称初始化块,属于类中的成员,类似于方法,将逻辑语句封装在方法体中,通过{}
包围起来。但和方法不同,没有方法名,没有参数,只有方法体,而且不用通过对象或类显式调用,二十加载类时,或创建对象时隐士调用。
基本语法:[修饰符] { 代码 };
修饰符可选,要写的话,也只能写static
代码块分为两类,使用static
修饰的叫静态代码块,没有static
修饰的叫普通代码块
逻辑语句可以为任何逻辑语句,(输入、输出、方法调用、循环、判断等)
;
可以写上,也可以省略
优点
相当于另一种形式的构造器,可以做初始化的操作
场景:如果多个构造器中都有重复的语句,可以抽取到初始化块中,提高代码的重用性
代码块的调用优先于构造器的调用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 package com.xzt.codeblock_;public class CodeBlock01 { public static void main (String[] args) { } } class Movie { private String name; private double price; private String director; { System.out.println("屏幕开启" ); System.out.println("广告开始" ); System.out.println("电影开始" ); }; public Movie (String name, double price, String director) { this .name = name; this .price = price; this .director = director; } public Movie (String name, double price) { this .name = name; this .price = price; } public Movie (String name) { this .name = name; } }
注意事项
用static
修饰的代码块也叫静态代码块,作用就是对类进行初始化,而且它随着类的加载而执行 ,并且只会执行一次 ,如果是普通代码块,每创建一个对象,就执行。
类什么时候被加载 ⭐
创建对象实例时(new)
创建子类对象实例时,父类也会被加载
使用类的静态成员时(静态属性、静态方法)
普通代码块,会在创建对象实例时被隐士调用,创建一次则执行一次,如果使用类的静态成员时,普通代码块并不会执行。
创建一个对象时,在一个类调用顺序是: ⭐⭐
调用静态代码块和静态属性初始化 (注意:静态代码块和静态属性初始化调用的优先级一样,如果有多个静态代码块和多个静态变量初始化,则按他们定义的先后顺序调用)
调用普通代码块和普通属性的初始化 (注意:普通代码块和普通属性初始化调用的优先级一样,如果有多个普通代码块和多个普通变量初始化,则按他们定义的先后顺序调用)
调用构造方法
构造器最前面其实隐含了super()
和 调用普通代码块,
创建一个子类时(继承关系),他们的静态代码块,静态属性初始化,普通代码块,普通属性初始化,构造方法的调用顺序如下: ⭐⭐
父类的静态代码块和静态属性初始化(优先级一样,按定义顺序执行)
子类的静态代码块和静态属性初始化(优先级一样,按定义顺序执行)
父类的普通代码块和普通属性初始化(优先级一样,按定义顺序执行)
父类的构造方法
子类的普通代码块和普通属性初始化(优先级一样,按定义顺序执行)
子类的构造方法
静态代码块只能直接调用静态成员(静态属性和静态方法),普通代码块可以调用任意成员
单例设计模式 设计模式 :设计模式是在大量的实践中总结和理论化之后的优选的代码结构、编程风格、以及解决问题的思考方式。
单例模式 就是采取一定的方法保证在整个软件系统中,对某个类只能存在一个对象实例,并且该类只提供一个取得对象实例的方法。
饿汉式 没有使用对象,但是加载类的时候就会创建对象。可能造成创建了对象,但没有使用,造成资源的浪费。
构造器私有化
类内部创建对象(该对象时static
)
向外暴露一个静态的公共方法,getInstance()
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 package com.xzt.single_;public class SingleTone01 { public static void main (String[] args) { GirlFriend gf = GirlFriend.getInstance(); System.out.println(gf); GirlFriend gf2 = GirlFriend.getInstance(); System.out.println(gf); } } class GirlFriend { private String name; private static GirlFriend gf = new GirlFriend ("qqy" ); private GirlFriend (String name) { this .name = name; } public static GirlFriend getInstance () { return gf; } @Override public String toString () { return "GirlFriend{" + "name='" + name + '\'' + '}' ; } }
懒汉式 在使用时创建对象,只有在用户使用getInstance()
方法时才返回对象,后面再次调用时会返回上次创建的对象,从而保证了单例。
构造器私有化
定义一个static
静态属性对象
定义一个public
的static
方法可以返回Cat对象
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 package com.xzt.single_;public class SingleTon02 { public static void main (String[] args) { Cat cat = Cat.getInstance(); System.out.println(cat.toString()); } } class Cat { private String name; private static Cat cat; private Cat (String name) { this .name = name; } public static Cat getInstance () { if (cat == null ){ cat = new Cat ("xzt" ); } return cat; } @Override public String toString () { return "Cat{" + "name='" + name + '\'' + '}' ; } }
饿汉式和懒汉式的区别
最主要的区别是创建对象的时机不同 ,饿汉式是在类加载就创建了对象实例,而懒汉式是在使用时才创建
饿汉式不存在线程安全问题,懒汉式存在线程安全问题
饿汉式存在浪费资源的问题 ,懒汉式则不存在这个问题。和对象的创建时机有很大的关系
在javaSE
标准类中,java.lang.Runtime
就是经典的单例模式
final
关键字final
可以修饰类、属性、方法和局部变量
使用场景
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 package com.xzt.final_;public class Final01 { public static void main (String[] args) { } } final class A {} class B extends A { }
当不希望父类的某个方法被子类重写/覆盖(override
)时,可以使用final
关键字修饰
1 2 3 4 5 6 7 8 9 10 11 class C { public final void hi () { } } class D extends C { @Override public void hi () { System.out.println("重写了hi方法" ); } }
当不希望类的某个属性的值被修改,可以使用final
修饰
当不希望某个局部变量被修改。可以使用final
修饰
注意事项
final
修饰的属性又叫常量,一般用XX_XX_XX
来命名
final
修饰的属性在定义时,必须赋初值,并且以后不能再修改,赋值可以再如下位置之一:
定义时,例如:public final double PI = 3.1415926;
在构造器中
在代码块中
如果final
修饰的属性是静态的,则初始化的位置只能是:
final
类不能被继承,但可以实例化对象。
如果类不是final
类,但是含有final
方法,则该方法虽然不能被重写,但是可以被继承。
一般来说,如果一个类是final
类了,就没必要再将方法修饰成final
方法
final
不能修饰构造器(构造方法)
final
和static
往往搭配使用,效率更高,底层编译器做了优化处理。调用类中的该属性时,不会加载类。
包装类(Integer
,Double
,Float
,Boolean
等都是final
类),String
也是final
类
抽象类 定义: 当父类的某些方法需要声明,但是又不能确定如何实现时,可以将其声明为抽象方法,那么这个类就是抽象类
用abstract
修饰的方法
所谓抽象方法就是没有实现的方法,没有实现就是指没有函数体
当一个类中存在抽象方法时,需要将该类声明为abstract
类
一般来说抽象类会被继承,由其子类实现抽象方法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 package com.xzt.abstract_;public class Abstract01 { public static void main (String[] args) { } } abstract class Animal { private String name; public Animal (String name) { this .name = name; } public abstract void eat () ; }
注意事项
抽象类不能被实例化
抽象类不一定要包含abstract
方法,
一旦类中包含了abstract
方法,则这个类必须声明为abstract
abstract
只能修饰类和方法,不能修饰属性和其它的。
抽象类可以有任意成员【抽象类的本质还是类】
抽象方法不能有主体,即不能实现。
如果一个类继承了抽象类,则它必须实现抽象类的所有抽象方法,除非它自己也声明为abstract
类
⭐final
不能被继承,static
不能被重写,所以abstract
不能和final
或者static
组合使用
⭐private
不能被重写,所以abstract
也不能时private
的
抽象类的最佳实践-模板设计模式 编写模板类,例子如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 package com.xzt.abstract_;abstract public class Template { abstract public void job () ; public void calculateTime () { long start = System.currentTimeMillis(); job(); long end = System.currentTimeMillis(); System.out.println("执行了" + (end - start)); } }
1 2 3 4 5 6 7 8 9 10 11 12 package com.xzt.abstract_;public class A extends Template { @Override public void job () { long num = 0 ; for (long i = 0 ; i < 800000 ; i++) { num += i; } } }
1 2 3 4 5 6 7 8 9 10 11 12 package com.xzt.abstract_;public class B extends Template { @Override public void job () { long num = 0 ; for (long i = 0 ; i < 1000000 ; i++) { num += i; } } }
1 2 3 4 5 6 7 8 9 10 11 12 package com.xzt.abstract_;public class TestTemplate { public static void main (String[] args) { A a = new A (); a.calculateTime(); B b = new B (); b.calculateTime(); } }
接口 接口就是给出一些没有实现的方法,封装到一起,到某个类要使用的时候,再根据具体情况把这些方法写出来。
基本语法: interface 接口名{ //属性 // 方法}
实现方法: class 类名 implements 接口名 { // 自己的属性 // 自己的方法 //必须实现的接口的抽象方法}
总结:
在Jdk7.0
前,接口里的所有方法都没有方法体,即都是抽象方法
Jdk8.0
后接口可以有静态方法,默认方法,也就是说接口中可以有方法的具体实现
1 2 3 4 5 6 7 8 9 package com.xzt.interface_;public interface AInterface { public int n = 10 ; public void hi () ; }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 package com.xzt.interface_;public class Interface01 { public static void main (String[] args) { } } class A implements AInterface { @Override public void hi () { } }
注意事项
接口不能被实例化
接口中所有方法时public
方法,接口中抽象方法,可以不用abstract
修饰
一个普通类实现接口,就必须将该接口的所有方法都是实现
抽象了实现接口,可以不用实现接口的方法
一个类可以同时实现多个接口
接口中的属性只能时final
,而且时public static final
修饰符;例如:int a = 1
实际上是public static final int a = 1
接口中的属性的访问形式:接口名.属性名
接口中不能继承其他的类,但是可以继承多个别的接口 。interface A extends B, C {}
接口的修饰符只能是public
和默认,这点和类的修饰符是一样的
实现接口 VS 继承类
实现接口是对单继承机制的补充
继承:先天拥有,当子类继承了父类,则自动拥有了父类的能力,
实现:后天学习,如果子类需要扩展某些功能,则需要实现接口的方式进行获取。
接口和问题解决的问题不同:
继承的价值在于解决代码的复用性和可维护性
接口的价值在于:设计,设计好各种规范(方法),让其他类去实现这些方法
接口比继承更加灵活
接口在一定程度上可以实现代码解耦【即:接口规范 + 动态绑定机制】
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 package com.xzt.interface_;import java.lang.reflect.Modifier;public class ExtendsVSInterface { public static void main (String[] args) { LittleMonkey wukong = new LittleMonkey ("悟空" ); wukong.climbing(); wukong.swimming(); wukong.fly(); } } class Monkey { private String name; public Monkey (String name) { this .name = name; } public void climbing () { System.out.println(name + "猴子会爬树" ); } public String getName () { return name; } } interface Fish { void swimming () ; } interface Bird { void fly () ; } class LittleMonkey extends Monkey implements Fish , Bird { public LittleMonkey (String name) { super (name); } @Override public void swimming () { System.out.println(getName() + " 通过学习会游泳了" ); } @Override public void fly () { System.out.println(getName() + " 通过学习会飞翔了" ); } }
接口的多态
多态参数 ,可以接受实现了该接口的所有类的实例对象,接口类型的变量可以指向实现了该接口的对象。和继承体现的多态类似。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 package com.xzt.interface_;public class InterfacePolyParameter { public static void main (String[] args) { IF if01 = new Monster (); IF if02 = new Car (); } } interface IF {}class Monster implements IF {}class Car implements IF {}
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 package com.xzt.interface_;public class InterfacePolyArr { public static void main (String[] args) { Usb[] usbs = new Usb [2 ]; usbs[0 ] = new Camera (); usbs[1 ] = new Phone (); for (int i = 0 ; i < usbs.length; i++) { usbs[i].work(); if (usbs[i] instanceof Phone){ ((Phone) usbs[i]).call(); } } } } interface Usb { void work () ; } class Phone implements Usb { public void call () { System.out.println("手机打电话" ); } @Override public void work () { System.out.println("手机工作中" ); } } class Camera implements Usb { @Override public void work () { System.out.println("相机工作中" ); } }
多态传递 :如果接口A继承了接口B,而类C仅仅实现了接口A,则相当于类C也实现了接口B。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 package com.xzt.interface_;public class InterfacePolyPass { public static void main (String[] args) { IG ig = new Teacher (); IH ih = new Teacher (); } } interface IH { void hi () ; } interface IG extends IH {}class Teacher implements IG { @Override public void hi () { } }
注意: 当类C继承了类A,实现了接口B,并且类A和接口B中有同名的属性,直接调用时则会造成模糊,所以调用父类的可以使用 super.属性名
,调用接口的可以使用接口名.属性名
。
类的五大成员: 属性、方法、构造器、代码块、内部类。
内部类 一个类的内部又完整的嵌套了另一个类结构,被嵌套的类称为内部类(inner class
),嵌套其他类的类称为外部类(outer class
)。是我们类的第五大成员。内部类最大的特点就是可以访问私有属性,并且可以体现类与类之间的包含关系。
1 2 3 4 5 6 7 8 9 class Outer { class Inner { } } class Other { }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 package com.xzt.innerclass;public class InnerClass01 { public static void main (String[] args) { } } class Outer { private int n1 = 100 ; public Outer (int n1) { this .n1 = n1; } public void m1 () { System.out.println("m1()" ); } { System.out.println("代码块" ); } class inner { } }
内部类的分类
定义在外部类局部位置上(比如方法内):
局部内部类(有类名)
匿名内部类(没有类名,重点!!!)⭐⭐
定义在外部类的成员位置上
成员内部类(没用static
修饰)
静态内部类(用static
修饰)
局部内部类 定义在外部类的局部位置,比如方法中,并且有类名。
可以直接访问外部类的所有成员,包含私有的。
不能添加访问修饰符,局部变量不能添加访问修饰符,可以使用final
修饰,局部变量可以使用final
修饰
作用域: 仅仅在定义它的方法或代码块中。
局部内部类 直接访问 外部类成员
外部类 访问 局部内部类的成员,访问方式:创建对象,再访问(注意:必须在作用域内)
外部其他类 不能访问 局部内部类(因为局部内部类是一个局部变量)
如果外部类和局部内部类的成员重名时,默认遵循就近原则,如果像访问外部类的成员,则可以使用(外部类.this.成员
)去访问
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 package com.xzt.innerclass;public class LocalInnerClass { public static void main (String[] args) { Outer02 outer02 = new Outer02 (); outer02.m1(); } } class Outer02 { private int n1 = 10 ; private void m2 () {} public void m1 () { class Inner02 { private int n1 = 100 ; public void f1 () { System.out.println("n1 = " + n1); System.out.println(Outer02.this .n1); m2(); } } Inner02 inner02 = new Inner02 (); inner02.f1(); } { class Inner03 { } } }
匿名内部类 ⭐⭐⭐ 匿名内部类是定义在外部类的局部位置,比如方法中,并且没有名字。
本质:是一个类
,且是一个内部类
,该类没有名字,同时还是一个对象。
基本语法:new 类或接口(参数列表) {}
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 package com.xzt.innerclass;public class AnonymousInnerClass { public static void main (String[] args) { Outer04 outer04 = new Outer04 (); outer04.method(); } } class Outer04 { private int n1 = 10 ; public void method () { A Tiger = new A () { @Override public void cry () { System.out.println("老虎" ); } }; Tiger.cry(); System.out.println("Tiger的运行类型 = " + Tiger.getClass()); Father father = new Father ("jack" ) { @Override public void Test () { System.out.println("匿名内部类重写了Test()方法" ); } }.Test(); System.out.println("father的运行类型 = " + father.getClass()); } } interface A { public void cry () ; } class Father { public Father (String name) { } public void Test () { } }
注意事项
匿名内部类既是一个类的定义,同时它本身也是一个对象,因此从语法上看,它既有定义的特征,也有创建对象的特征。所以调用方法有两种,看上面的示例。
可以直接访问外部类的所有成员,包括私有成员。
不能添加访问修饰符,因为它是一个局部变量
作用域:仅仅在定义它的方法或代码块中
匿名内部类 直接访问 外部类成员
外部其它类 不能访问 匿名内部类,因为匿名内部类是一个局部变量,
如果外部类的成员和匿名内部类的成员重名时,匿名内部类访问的话,默认遵循就近原则,如果像访问外部类的成员,则可以使用外部类名.this.成员
去访问。
成员内部类 定义在外部类的成员位置,并且没有static
修饰,
可以直接访问外部类的所有成员,包含私有的
可以添加访问修饰符(public
、private
、默认
、protected
),因为他的地位就是一个成员。
作用域:和外部类的其他成员一样,为整个类体。
成员内部类 直接访问 外部类(比如:属性)【访问方式:直接访问】
外部类 简介访问 内部类,访问方式:创建对象,再访问
外部其他类 访问 内部类,有两种访问方式:
Outer08.Inner08 inner08 = outer08.new Inner08();
Outer08.Inner08 inner081 = outer08.getInner08Instance();
如果外部类和内部类中的成员重名时吗,内部类访问的话,默认遵循就近原则,如果想访问外部类的成员,则可以使用(外部类.this.成员
)去访问。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 package com.xzt.innerclass;public class MenberInnerClass { public static void main (String[] args) { Outer08 outer08 = new Outer08 (); outer08.t1(); Outer08.Inner08 inner08 = outer08.new Inner08 (); Outer08.Inner08 inner081 = outer08.getInner08Instance(); } } class Outer08 { private int n1 = 10 ; public String name = "张三" ; public class Inner08 { public void say () { System.out.println("Outer08 的 n1 = " + n1 + "Outer08 的 name = " + name); } } public Inner08 getInner08Instance () { return new Inner08 (); } public void t1 () { Inner08 inner08 = new Inner08 (); inner08.say(); } }
静态内部类 静态内部类是定义在外部类的成员位置,并且有static
修饰
可以直接访问外部类的所有静态成员,包括私有的,但不能直接访问非静态成员
可以添加任意的访问修饰符,因为它是成员
作用域:同其他成员,为整个类体
静态内部类 直接访问 外部类的所有静态成员
外部类 访问 静态内部类 需要创建对象,再访问。
外部其他类 使用 静态内部类:三种方式
Outer10.Inner10 inner10 = new Outer10.Inner10();
Outer10.Inner10 inner10Instance = new Outer10().getInner10Instance();
创建对象,调用方法
Outer10.Inner10 inner10Instance1 = Outer10.getInner10Instance();
直接使用类名调用静态方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 package com.xzt.innerclass;public class StaticInnerClass { public static void main (String[] args) { Outer10.Inner10 inner10 = new Outer10 .Inner10(); Outer10.Inner10 inner10Instance = new Outer10 ().getInner10Instance(); Outer10.Inner10 inner10Instance1 = Outer10.getInner10Instance(); } } class Outer10 { private int n1 = 10 ; private static String name = "张三" ; static class Inner10 { public void say () { System.out.println(name); } } public static Inner10 getInner10Instance () { return new Inner10 (); } }
枚举和注解 枚举类
枚举是一组常量的集合
枚举属于一种特殊的类,里面只包含一组有限的特定的对象
两种实现方式:
自定义类实现枚举
将构造器私有化,目的是防止直接new
去掉setXXX
方法,防止属性被修改,因为枚举对象通常为只读
在类内部直接创建固定的对象
对枚举对象/属性使用static + final
共同修饰,实现底层优化
枚举对象名通常使用全部大写,常量的命名规范
枚举对象根据需要,也可以有多个属性
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 package com.xzt.enum_;public class Enumeration01 { public static void main (String[] args) { System.out.println(Season.SPRING); System.out.println(Season.AUTUMN); } } class Season { private String name; private String desc; public static final Season SPRING = new Season ("春天" , "温暖" ); public static final Season WINTER = new Season ("冬天" , "寒冷" ); public static final Season SUMMER = new Season ("夏天" , "酷热" ); public static final Season AUTUMN = new Season ("秋天" , "凉爽" ); private Season (String name, String desc) { this .name = name; this .desc = desc; } public String getName () { return name; } public String getDesc () { return desc; } }
enum
关键字实现枚举实现步骤:
使用enum
关键字替换class
关键字
SPRING("春天", "温暖");
代替 public static final Season SPRING = new Season("春天", "温暖");
创建对象
如果有多个常量(对象),使用,
间隔
如果使用enum
来实现枚举,要求将定义常量对象,写在最前面。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 package com.xzt.enum_;public class Enumeration02 { public static void main (String[] args) { System.out.println(Season02.AUTUMN); System.out.println(Season02.SPRING); } } enum Season02 { SPRING("春天" , "温暖" ), WINTER("冬天" , "寒冷" ), SUMMER("夏天" , "酷热" ), AUTUMN("秋天" , "凉爽" ), WHAT; private String name; private String desc; private Season02 (String name, String desc) { this .name = name; this .desc = desc; } private Season02 () {} public String getName () { return name; } public String getDesc () { return desc; } @Override public String toString () { return "Season02{" + "name='" + name + '\'' + ", desc='" + desc + '\'' + '}' ; } }
注意事项
当我我们enum
关键字开发一个枚举类时,默认会继承Enum
类,而且是一个final
类,使用javap
工具进行验证
定义对象时被简化
如果使用的是无参构造器 创建 枚举对象,则实参列表和小括号都可以省略。
当有多个枚举对象时,使用,
间隔,最后一个分号结束
枚举对象必须放在枚举类的行首
当使用enum
关键字后,就不能继承其他类,因为enum
会隐式继承Enum
类
enum
实现的枚举类,仍然是一个类,所以还是可以实现接口的
Enum
类中的方法
toString()
: Enum
类已经重写过了,返回的时当前对象名
name()
:输出枚举对象的名称
ordinal()
:输出该枚举对象的次序,从0开始编号
values()
:将所有定义的所有枚举对象以数组形式返回
valueOf()
:将字符串转成枚举对象,但要求字符串必须为已有的常量名,否则报异常。
compareTo()
:比较两个枚举常量,比较就是编号 retrun self.ordinal - other.ordinal;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 package com.xzt.enum_;public class EnumMethod { public static void main (String[] args) { Season02 autumn = Season02.AUTUMN; System.out.println(autumn.name()); System.out.println(autumn.ordinal()); Season02[] values = Season02.values(); for (Season02 season: values) { System.out.println(season); } Season02 summer = Season02.valueOf("SUMMER" ); System.out.println(summer); System.out.println(Season02.AUTUMN.compareTo(Season02.SUMMER)); } }
注解 注解(Annotation
)也被称为元数据(Metadata
),用于修饰解释 包、类、方法、属性、构造器、局部变量等数据信息。
和注释一样,注解不影响程序逻辑,但注解可以被编译或运行,相当于嵌入在代码中的补充信息。
在JavaSE
中,注解的使用目的比较简单,例如标记过时的功能,忽略警告等。在JavaEE
中注解占据了更重要的角色,例如用来配置应用程序的任何切面,代替Java EE
旧版中所遗留的繁冗代码和XML配置等。
使用Annotation
时要在其前面增加@
符号,并把该Annotation
当成一个修饰符使用。用于修饰它支持的程序元素。
三个基本的Annotation
JDK的元注解(了解) JDK的元Annotation用于修饰其它Annotation
异常 Exception
Java语言中将程序执行中发生的不正常情况称为”异常”。(语法错误和逻辑错误不是异常)
执行过程中异常可以分为两类:
Error
(错误):Java虚拟机无法解决的严重问题。如:JVM系统内部错误、资源耗尽等情况,
Exception
:其它因编程错误或偶然的外在因素导致的一般性问题,可以使用针对性的代码进行处理。
运行时异常:程序运行时发生的异常
编译时异常:程序编程时,由编译器检查出的异常。
运行时异常 编译器不要求强制处置的异常。一般是指编程时的逻辑错误没事成簇元应该避免其出现的异常。java.lang.RuntimeException
类及它的子类都是运行时异常。
对于运行时异常,可以不作处理,因为这类异常很普遍,若全处理可能对程序的可读性和运行效率产生影响。
五大运行时异常
NullPointerException
空指针异常,当应用程序试图在需要对象的地方使用null时,抛出该异常。
1 2 3 4 5 6 7 8 9 10 11 12 package com.xzt.exception_;public class NullPointerException_ { public static void main (String[] args) { String name = null ; System.out.println(name.length()); } }
ArithmeticException
数学运算异常,当出现异常的运算条件时,抛出此异常。
ArryIndexOutOfBoundsException
数组越界异常,用非法索引访问数组时抛出异常。
ClassCastException
类型转换异常,当试图将对象强制转换成不是实例的子类时抛出异常。
NumberFormatException
数字格式不正确异常,当应用程序试图将字符串转换成一种数值类型,但该字符串必能准换为适当格式时,抛出异常。
编译时异常 是编译器要求必须处置的异常。
异常处理
当异常发生时,对异常处理的方式
try - catch - finally
程序员在代码中捕获发生的异常,自行处理。
1 2 3 4 5 6 7 8 9 10 11 try { }catch (Exception e){ }finally { }
注意事项
如果异常发生了,则异常后买你的代码不会执行,直接进入到catch
块,
如果异常没有发生,则顺序执行try
的代码块,不会进入到catch
如果希望不管是否发生异常,都执行某段代码(比如关闭连接,释放资源等)则使用finally
throws
将发生的异常抛出,交给调用者(方法)来处理,最顶级的处理者就是JVM
当下层方法出现异常时,可以将异常抛出至上层调用自己的方法。直至抛出至JVM,JVM处理异常方法:输出异常信息,中断程序,退出程序。
如果程序员没有显示的处理异常,默认使用throws
处理异常
注意事项
对于编译异常,程序中必须处理,比如try - catch
或者throws
对于运行异常,程序中如果没有处理,默认就是throws
方式处理
子类重写父类方法时,对抛出异常的规定:子类重写的方法,所抛出的异常类型要么和父类抛出的异常一致,要么为父类抛出的异常的类型的子类。
在throws
过程中,如果有方法try-catch
,就相当于处理异常,就可以不必throws
自定义异常
当程序中出现了某些“错误”,但该错误信息并没有在Trowable子类中描述处理,这个时候可以自己设计异常类,用于描述该错误信息。
步骤
定义类:自定义异常类名(程序员自己写),继承Exception
或RuntimeException
如果继承Exception
,属于编译异常。
如果继承RuntimeException
,属于运行异常(一般来说,继承RuntimeException
)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 package com.xzt.customexception;public class CustomException { public static void main (String[] args) { int age = 80 ; if (!(age >= 18 && age <= 120 )) { throw new AgeException ("年龄需要在 18~120之间" ) } System.out.println("你的年龄范围正确。" ); } } class AgeException extends RuntimeException { public AgeException (String message) { super (message); } }
throw
和 throws
的区别
意义
位置
后面跟的东西
throws
异常处理的一种方式
方法声明处
异常类型
throw
手动生成异常对象的关键字
方法体中
异常对象
常用类 包装类
针对八种基本数据类型相应的引用类型——包装类
有了类的特点,就可以调用类中的方法
基本数据类型
包装类
boolean
Boolean
char
Character
byte
Byte
short
Short
int
Integer
long
Long
float
Float
double
Double
包装类和基本数据的转换
jdk5
前的手动装箱和拆箱方式,装箱:基本类型->包装类,反之为拆箱
jdk5
以后的自动装箱和拆箱方式
自动装箱底层调用的是valueOf()
方法,自动拆箱底层调用的是intValue()
方法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 package com.xzt.wrapper;public class WrapperType { public static void main (String[] args) { int n1 = 10 ; Integer integer = new Integer (n1); Integer integer1 = Integer.valueOf(n1); int i = integer.intValue(); int n2 = 200 ; Integer integer2 = n2; int n3 = integer2; } }
包装类型和String类型的相互转换 以Integer
和String
类型的相互转换为例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 package com.xzt.wrapper;public class WrapperVSString { public static void main (String[] args) { Integer integer = 100 ; String str1 = integer + "" ; String s = integer.toString(); String s1 = String.valueOf(integer); String str = "123456" ; Integer i = Integer.parseInt(str); Integer integer1 = new Integer (str); } }
包装类常用的方法 Integer
类和String
类为例
Integer.MIN_VALUE
,返回最小值
Integer.MAX_VALUE
,返回最大值
Character.isDigit('a')
,判断是不是数字
Character.isLetter('a')
,判断是不是字母
Character.isUpperCase('a')
,判断是不是大写
Character.isLowerCase('a')
,判断是不是小写
Character.isLowerCase('a')
,判断是不是小写
Character.isWhitespace('a')
,判断是不是空格
Character.toLowerCase('a')
,转成小写
Character.toUpperCase('a')
,转成大写
String类
String
对象适用于保存字符串,也就是一组字符序列
字符串常量对象是用双引号括起来的字符序列。如:”jack”
字符串的字符使用Unicode字符编码,一个字符(不区分字母还是汉字)占两个字节。
String
类有很多构造器,构造器的重载。
String
类实现了接口Serializable
,则String可以串行化(数据可以在网络传输),还实现了Comparable
接口,对象可以比较大小。
String
是final
类,不能被继承
String
有属性 private final char value[]
用于存放字符串内容。因为是final
类型,所以不可以修改(指代地址不能够修改)
创建String对象的两种方式
直接赋值String str = "xzt";
调用构造器String str = new String("xzt");
字符串的特性
String
类是一个final
类,代表不可变的字符序列
字符串不可变,一个字符串对象一旦被分配,其内容是不可变的。
String类的常见方法 String类是保存字符串常量的,每次更新都需要重新开辟空间,效率低,因此java设计者还提供了StringBuilder
和StringBuffer
来增强String的功能,并提高效率。
equals()
区分大小写,判断内容是否相等
length()
获取字符的个数,字符串的长度
indexOf()
获取第i个字符
substring(x)
,截取索引x后面的内容
substring(start, end)
,从索引start开始,一直到索引end-1的位置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 package com.xzt.string_;public class String01 { public static void main (String[] args) { String str = "jack" ; System.out.println(str.equals("JACK" )); System.out.println(str.equalsIgnoreCase("JACK" )); for (int i = 0 ; i < str.length(); i++) { System.out.println(str.indexOf(i)); } String s1 = "abcdfsadfrf" ; int a = s1.indexOf('a' ); String substring = s1.substring(0 , 6 ); } }
StringBuffer
类java.lang.StringBuffer
代表可变的字符序列,可以对字符串内容进行增删。很多方法和String相同,但StringBuffer
是可变长度的 。
类结构
直接父类是AbstractStringBuilder
类
实现了Serializable
,即StringBuffer
的对象可以串行化
在父类中 有属性 char[] value
,不是final
,该value数组存放字符串的内容。存放在堆中。
StringBuffer
是一个final
类,不能被继承。
StringBuffer
VS String
String
保存的是字符串常量,里面的值不能更改,每次String
类的更新实际上就是更改地址,效率低。
StringBuffer
保存的是字符串变量,里面的值可以更改,每次StringBuffer
的更新实际上可以更新内容,不用更新地址,效率高。
构造器
StringBuffer stringBuffer = new StringBuffer();
创建一个大小为16的char[]
,用于存放字符内容
StringBuffer stringBuffer = new StringBuffer(100);
,通过构造器指定char[]
大小
StringBuffer stringBuffer = new StringBuffer("Hello");
通过构造器给一个String
创建StringBuffer
,char[]
大小为String的长度 + 16
StringBuffer
的转换1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 package com.xzt.StringBuffer_;public class StringAndStringBuffer { public static void main (String[] args) { String str = "Hello Xzt" ; StringBuffer stringBuffer = new StringBuffer (str); StringBuffer stringBuffer1 = new StringBuffer (); stringBuffer1.append(str); String s = stringBuffer.toString(); String s1 = new String (stringBuffer); } }
StringBuilder
类一个可变的字符序列,此类提供一个与StringBuffer
兼容的API,但不保证同步。该类被设计用作StringBuffer
的一个简易替换,用在字符串缓冲区被单个线程使用的时候 。如果可能,建议优先采用该类,因为在大多数实现中,它比StringBuffer
要快。
在StringBuilder
上主要操作的是append()
和insert()
方法,可重载这些方法,以接受任意类型的数据。
Math类
Math类包含于执行基本数学运算的方法,如初等指数、对数、平方根和三角函数。
均是静态方法,因此可以直接使用Math.
调用。
方法
解释
abs(x)
计算x的绝对值
pow(x, y)
计算幂,x的y次方
ceil(x)
返回大于等于x的最小整数,向上取整
floor(x)
返回小于等于x的最小整数,向下取整
round(x)
将x进行四舍五入并返回
sqrt(x)
求x的开方
random()
返回的是0<= x < 1之间的一个随机小数
max(x, y)
求两个数的最大值
min(x, y)
求两个数的最小值
Arrays类 Arrays
l里面包含了一系列静态方法,用于管理或操作数组(比如排序和搜索)
常用的方法
函数
解释
Arrays.toString(arr)
遍历(显示)数组
Arrays.sort(arr);
默认从小到大将数组进行排序,可以通过Conparator
接口实现定制排序,排序不会改变原本的数组
Arrays.binarySearch(arr, x)
通过二分搜索进行查找,要求数组必须排好序
Arrays.copyOf(arr, x)
将arr数组中的x个元素拷贝至一个新数组中
Arrays.fill(arr, x)
使用x填充arr数组
Arrays.equals(arr, tem)
比较两个数组,如果数组中的元素一样,返回true
Arrays.asList(1,2,3,4)
会将数据转成一个List
集合并返回。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 package com.xzt.arrays_;import java.util.Arrays;import java.util.Comparator;public class Arrays01 { public static void main (String[] args) { Integer[] integers = {1 , 20 , 90 }; for (int i = 0 ; i < integers.length; i++) { System.out.println(integers[i]); } System.out.println(Arrays.toString(integers)); Integer[] arrs = {1 , -1 , 7 , 0 , 89 }; Arrays.sort(arrs); System.out.println(Arrays.toString(arrs)); Arrays.sort(arrs, new Comparator <Integer>() { @Override public int compare (Integer o1, Integer o2) { return o2 - o1; } }); System.out.println(Arrays.toString(arrs)); System.out.println(Arrays.binarySearch(arrs, -1 )); } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 package com.xzt.arrays_;import java.util.Arrays;import java.util.Comparator;public class ArraysSortCustom { public static void main (String[] args) { int [] arr = {1 , -1 , 8 , 0 , 20 }; bubbleSort(arr, new Comparator () { @Override public int compare (Object o1, Object o2) { int i1 = (Integer) o1; int i2 = (Integer) o2; return i1 - i2; } }); } public static void bubbleSort (int [] arr) { int temp = 0 ; for (int i = 0 ; i < arr.length - 1 ; i++) { for (int j = 0 ; j < arr.length - i - 1 ; j++) { if (arr[i] > arr[i + 1 ]) { temp = arr[i]; arr[i] = arr[i + 1 ]; arr[i + 1 ] = temp; } } } } public static void bubbleSort (int [] arr, Comparator c) { int temp = 0 ; for (int i = 0 ; i < arr.length - 1 ; i++) { for (int j = 0 ; j < arr.length - i - 1 ; j++) { if (c.compare(arr[i], arr[i + 1 ]) > 0 ) { temp = arr[i]; arr[i] = arr[i + 1 ]; arr[i + 1 ] = temp; } } } } }
System类 常用方法
System.exit(0)
退出当前程序
arraycopy()
:赋值数组元素,比较适合底层调用,一般使用Arrays.copyOf
完成复制数组
System.currentTimeMillens()
:返回当前时间距离1970-1-1的毫秒数
System.gc()
:运行垃圾回收机制。
BigInteger 和 BigDecimal 类
BigInteger 适合保存比较大的整型
BigDecimal 适合保存精度更高的浮点型(小数)
BigInteger 类
不能直接进行加减乘除,需要使用提供的相应方法进行操作。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 package com.xzt.biginteger_;import java.math.BigInteger;public class BigInteger_ { public static void main (String[] args) { BigInteger bigInteger = new BigInteger ("123456789987654321" ); BigInteger bigInteger1 = new BigInteger ("100" ); System.out.println(bigInteger); BigInteger add = bigInteger.add(bigInteger1); System.out.println(add); BigInteger subtract = bigInteger.subtract(bigInteger1); BigInteger multiply = bigInteger.multiply(bigInteger1); BigInteger divide = bigInteger.divide(bigInteger1); } }
BigDecimal 类
使用方法和BigInteger
类似,
除法可能会产生异常,因为可能会产生无线循环小数,所以需要在后面指定精度。会保留分子精度。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 package com.xzt.bigdecimal;import java.math.BigDecimal;public class BigDecimal_ { public static void main (String[] args) { BigDecimal bigDecimal = new BigDecimal ("12.15626498411494692196496265965269560000" ); System.out.println(bigDecimal); BigDecimal bigDecimal1 = new BigDecimal ("1.1" ); System.out.println(bigDecimal.add(bigDecimal1)); System.out.println(bigDecimal.subtract(bigDecimal1)); System.out.println(bigDecimal.multiply(bigDecimal1)); System.out.println(bigDecimal.divide(bigDecimal1, BigDecimal.ROUND_CEILING)); } }
日期类 第一代日期类
Date : 可以精确到毫秒,代表特定的瞬间
SimpleDateFormat
:格式和解析日期的类。它允许进行格式化(日期->文本)解析(文本->日期)
Date date = new Date()
,这里的Date
类是在java.util
包
默认输出的日期格式是国外的方式,因此需要对格式进行转换。
SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日 hh:mm:ss E");
用于将日期格式化为自定义格式。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 package com.xzt.date_;import java.text.ParseException;import java.text.SimpleDateFormat;import java.util.Date;import java.util.logging.SimpleFormatter;public class Date_ { public static void main (String[] args) { Date date = new Date (); System.out.println(date); SimpleDateFormat sdf = new SimpleDateFormat ("yyyy年MM月dd日 hh:mm:ss E" ); String format = sdf.format(date); System.out.println(format); Date date1 = new Date (9234567 ); System.out.println(date1); String s = "1997年01月01日 10:20:30 星期五" ; try { Date parse = sdf.parse(s); System.out.println(sdf.format(parse)); } catch (ParseException e) { e.printStackTrace(); } } }
第二代日期类
主要就是Calendar类(日历) ,Calendar类是一个抽象类,为特定瞬间与一组诸如YEAR、MONTH、DAY_OF_MONTH、HOUR等日历字段之间的转换提供了一些方法,并为操作日历字段提供了一些方法。
Calendar
是一个抽象类,并且构造器是private
,可以通过getInstance
方法来获取实例。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 package com.xzt.date_;import java.util.Calendar;public class Calendar_ { public static void main (String[] args) { Calendar instance = Calendar.getInstance(); System.out.println(instance); System.out.println(instance.get(Calendar.YEAR)); System.out.println(instance.get(Calendar.MONTH) + 1 ); System.out.println(instance.get(Calendar.DAY_OF_MONTH)); System.out.println(instance.get(Calendar.HOUR)); } }
第三代日期类 Calendar
类存在的问题:
可变性:像日期和时间这样的类应该是不可变的。
偏移性:Date
中的年份是从1900年开始的,而月份都从0开始
格式化:格式化支队Date
有用,Calendar
则不行
此外,他们也不是线程安全的;不能处理闺秒等(每隔两天,多出一秒)
第三代日期类常见方法
LocalDate
(日期/年月日)、LocalTime
(时间/时分秒)、LocalDateTime
(日期时间,JDK8加入)
LocalDateTime now = LocalDateTime.now();
获得当前时间
使用DateTimeFormatter
格式日期类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 package com.xzt.date_;import java.time.LocalDate;import java.time.LocalDateTime;import java.time.format.DateTimeFormatter;public class LocalDateTime_ { public static void main (String[] args) { LocalDateTime now = LocalDateTime.now(); System.out.println(now); System.out.println(now.getMonthValue()); DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy年MM月dd日 HH时mm分ss秒" ); String format = dtf.format(now); System.out.println(format); } }
集合 优点
可以动态保存任意多个对象,使用比较方便
提供了一系列方便的操作对象的方法:add、remove、set、get等
使用集合添加,删除新元素的示意代码简洁
集合的框架体系⭐⭐
Java 的集合类很多,主要分为两大类:单列集合、双列集合(键值对类型)
单列集合 Collection
Collection
接口实现类的特点public interface Collection<E> extends iterable<E>
Collection
实现子类可以存放多个元素,每个元素可以是Object
有些Collection
的实现类,可以存放重复的元素,有些不可以
有些Collection
的实现类,有些是有序的(List
),有些是无序的(Set
)
Collection
接口没有直接的实现子类,是通过它的子接口Set
和List
来实现的
常用的方法
方法
解释
add(x)
添加元素
remove(x)
删除元素
contains(x)
查找某个元素是否存在
size()
返回元素的个数
isEmpty()
判断是否为空
clear()
清空列表
addAll(结合)
添加多个元素
containsAll(集合)
查看多个元素是否都存在
removeAll(集合)
删除多个元素
遍历元素的方式 使用iterator
迭代器进行遍历
Iterator iterator = list.iterator();
生成一个迭代器
iterator.hashNext()
判断是否还存在下一个元素
Object next = iterator.next();
返回下一个元素,以Object
类型返回
遍历一遍后,如果需要进行再次遍历,需要重置迭代器iterator = list.iterator();
增强for循环
进行遍历
底层仍然是迭代器,是简化版的迭代器。
for(Object obj : list) {}
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 package com.xzt.collection_;import java.util.ArrayList;import java.util.Iterator;import java.util.List;public class CollectionMethod { public static void main (String[] args) { Collection list = new ArrayList (); list.add("xzt" ); list.add("qqy" ); list.add(20 ); list.add(true ); Iterator iterator = list.iterator(); while (iterator.hasNext()){ Object next = iterator.next(); System.out.println(next); } iterator = list.iterator(); while (iterator.hasNext()) { Object next = iterator.next(); System.out.println(next); } for (Object obj: list) { System.out.println(obj); } } }
List
List
集合类中元素有序(即添加顺序和取出顺序一致)、且可重复
List
集合中的每个元素都有其对应的顺序索引,即支持索引,索引是从0开始
List
的实现类有很多,例如:ArrayList
、Vector
、Stack
、LinkedList
等等
常用方法
方法
解释
add(int index, Object obj)
在index位置插入obj
addAll(int index, Collection col)
在index位置开始插入col中的所有元素
get(int index)
获得index位置的元素
indexOf(Onject obj)
返回obj在集合中首次出现的位置
lastIndexOf(Object obj)
返回obj在当前集合中最后出现的位置
remove(int index)
移除index位置的元素
set(int index, Object obj)
设置指定位置的元素为obj,相当于是替换
三种遍历方式
使用于所有的List
子类,前两种和上面Collection
类的遍历方法相同
使用iterator
迭代器遍历
使用增强for循环
进行遍历
使用for循环
进行遍历
ArrayList
注意事项
可以存放各种类型的元素,甚至包括空元素null
是由数组来实现数据存储的
基本等同于Vector
,但ArrayList
是线程不安全的(执行效率高),在多线程情况下,不建议使用ArrayList
底层操作机制 ⭐⭐
查看源码,进行分析
ArrayList
中维护的是一个Object
类型数组elementData
,transient Object[] elementData
,transient 表示该属性不会被序列化
当创建ArrayList
时,如果使用的是无参构造器,则初始elementData
容量为0,第一次添加,则扩容至10,如需再次扩容,则扩容为之前的1.5倍。
如果使用的是指定大小的构造器,则初始化elementData
的容量为指定大小,如果需要扩容,则直接扩容至1.5倍
Vector
Vector
底层也是一个对象数组,protected Object[] elementData
Vector
是线程同步的,即线程安全的,Vector
类的操作方法带有synchronized
,若需要线程同步,则考虑使用Vector
LinkedList
底层实现了双向链表 和双端队列 特点
可以添加任意元素(元素可以重复),包括null
线程不安全,没有实现同步
底层操作机制 ⭐⭐
底层维护了一个双向链表
维护了两个属性first
和last
分别指向首节点和尾节点
每个节点(Node对象),里面又维护了prev
、next
、item
三个属性,其中通过prev
指向前一个节点,通过next
指向后一个节点,最终实现了双向链表。
所以LinkedList
的元素的添加和删除,不是通过数组完成的,相对来说效率较高
ArrayList
和LinkedList
比较
底层结构
增删的效率
改查的效率
ArrayList
可变数组
较低,数组扩容
较高
LinkedList
双向链表
较高,通过链表追加
较低
如何选择ArrayList
和LinkedList
如果我们改查的操作多,选择ArrayList
如果我们增删的操作多,选择LinkedList
一般来说,在程序中,80%~90%都是查询,因此大部分情况选择ArrayList
在一个项目中,根据业务灵活选择,也可能这样,一个模块使用ArrayList
,另一个模块是LinkedList
Set
无序(添加和取出的顺序不一致),没有索引,取出的顺序虽然不是添加的顺序,但是是固定的。
不允许重复元素,所以最多包含一个null
JDK API中Set
接口的实现类有:HashSet
、TreeSet
等等
set
接口常用的方法
和List
接口一样,Set
接口也是Collection
的子接口,因此,常用方法和Collection
接口一样。
Set
接口的遍历方式
使用iterator
迭代器遍历
使用增强for循环
进行遍历
不能使用 索引的方式来获取
HashSet
HashSet
实现了Set
接口
HashSet
底层实际上是HashMap
可以存放null
,但最多只能存放一个
不保证元素是有序的,取决于hash后,在确定索引的结果,即不保证存放元素的顺序和取出顺序一致
不能有重复元素,
扩容机制 ⭐⭐
底层是HashMap
添加一个元素时,先得到hash值,会转成索引值
找到存储数据表table,看这个索引位置是否已经存放的有元素,如果没有,直接加入,如果有,调用equals
比较,如果相同,就放弃添加,反之,则添加到最后。
在Java 8中,如果一条链表的元素个数超过TREEIFY_THRESHOLD
(默认是8),并且table的大小 >= MIN_TREEIFY_CAPACITY
(默认是64),就会进行树化(红黑树)
LinkedHashSet
继承了HashSet
,是HashSet
的子类
底层是一个LinkedHashMap
,底层维护的是一个 数组 + 双向链表
LinkedHashSet
根据元素的hashCode
值来决定元素的存储位置,同时使用链表维护元素的次序,这使得元素看起来是以插入顺序保存的。
LinkedHashSet
不允许添加重复元素
TreeSet
当使用无参构造器创建TreeSet
时,它默认按字典序进行排序。
使用TreeSet
提供的一个构造器,可以传入一个比较器(匿名内部类),并指定排序规则。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 package com.xzt.set_;import java.util.Comparator;import java.util.TreeSet;public class TreeSet_ { public static void main (String[] args) { TreeSet treeSet = new TreeSet (new Comparator () { @Override public int compare (Object o1, Object o2) { return ((String) o1).compareTo((String) o2); } }); treeSet.add("jack" ); treeSet.add("tom" ); treeSet.add("xzt" ); treeSet.add("qqy" ); treeSet.add("z" ); System.out.println(treeSet); } }
双列集合 (K - V)
双列集合里面存放的是键值对类型(Key - Value)
Map 特点
Map
与Collection
并列存在,用于保存具有映射关系的数据:Key - Value
Map
中的 key 和 value 可以是任何引用类型的数据, 会封装在HashMap$Node
对象中
Map
中的 key 不允许重复,当有相同的 key 时,就等价于替换value
Map
中的 value 可以重复
Map
中的 key 可以为null,value 也可以为 null ,注意 key 为 null 只能有一个。value为null 可以有多个
常用String类作为Map
的 key
key 和 value 是一对一的关系,所以根据指定 key 能找到对应的 value
常用方法
方法
解释
put(K, V)
添加元素,存入键值对
remove(K)
、remove(K, V)
根据键删除映射关系
get(K)
根据键获取对应的值
size()
获取元素个数
isEmpty()
判断是否为空
clear()
清楚
containsKey(K)
查找键是否存在
遍历方式
先通过 keySet
取出所有的 Key 再根据 Key 取出相对应的 Value
1 2 3 4 5 6 7 8 9 10 11 12 Set set = map.keySet(); for (Object obj : set) { System.out.println(obj + "-" + map.get(obj)); } Iterator iterator = set.iterator();while (iterator.hasNext()){ Object next = iterator.next(); System.out.println(next + "-" + map.get(next)); }
1 2 3 4 5 6 7 8 9 10 11 12 Collection values = map.values();Iterator iterator1 = values.iterator();while (iterator1.hasNext()){ Object next = iterator1.next(); System.out.println(next); } for (Object obj : values) { System.out.println(obj); }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 Set entrySet = map.entrySet();for (Object entry : entrySet) { Map.Entry m = (Map.Entry) entry; System.out.println(m.getKey() + "-" + m.getValue()); } Iterator iterator2 = entrySet.iterator();while (iterator2.hasNext()){ Object next = iterator2.next(); Map.Entry m = (Map.Entry) next; System.out.println(m.getKey() + "-" + m.getValue()); }
HashMap
与HashSet
一样,不保证映射的顺序,因为底层是以hash表的方式来存储的,JDK 8的hashMap
底层是数组 + 链表 + 红黑树
HashMap
没有实现同步,因此是线程不安全的。方法上没有做同步互斥的操作,没有synchronized
关键字
底层机制
HashMap
的扩容机制和HashSet
完全一样
HashMap
底层维护了Node类型的数组table,默认为null
当创建对象时,将加载因子(load factor)初始化为0.75
当添加 key - value 时,通过key的哈希值的到再table表中的索引,然后判断该索引处是否有元素,如果没有元素直接添加,如果该索引处有元素,继续判断该元素的key是否和准备加入的key相等,如果相等,则直接替换value;如果不相等则需要判断是树结构还是链表结构,做出相应的处理。如果添加时发现容量不够,则需要扩容。
第一次添加,则需要扩容table容量为16,临界值(threshold)为12
以后再扩容,指责需要扩容table容量为原来的2倍,临界值为原来的2倍,即24,依此类推。
在Java 8中,如果一条链表的元素个数超过TREEIFY_THRESHOLD
(默认是8),并且table的大小 >= MIN_TREEIFY_CAPACITY
(默认是64),就会进行树化(红黑树)
HashTable
存放的元素是键值对:即 K - V
HashTable
的键和值都不能为null
,否则会抛出NullPointerException
HashTable
使用方法基本上和HashMap
一样
是线程安全的。有synchronized
修饰
Properties
继承自HashTable
类,并且实现了Map接口,也是使用一种键值对的形式来保存数据
它的使用特点和HashTable
类似
还可以用于从XXX.properties
文件中,加载数据到Properties
类对象,并进行读取和修改
说明:XXX.properties
通常为配置文件
TreeMap
当使用无参构造器创建TreeMap
时,它默认按 Key 的字典序进行排序。
使用TreeMap
提供的一个构造器,可以传入一个比较器(匿名内部类),并指定排序规则。
集合选型规则⭐
先判断存储类型,(一组对象[单列]或一组键值对[多列])
一组对象:Collection
接口
允许重复:List
增删多:LinkedList
【底层维护了一个双向链表】
改查多:ArrayList
【底层维护了Object类型可变数组】
不允许重复:Set
无序:HashSet
【底层是HashMap
,维护了一个哈希表,即 数组 + 链表 + 红黑树】
排序:TreeSet
插入和取出顺序一致:LinkedHashSet
【维护 数组 + 双向链表】
一组键值对:Map
键无序:HashMap
【底层是:哈希表 JDK 7:数组 + 链表;JDK 8:数组 + 链表 + 红黑树】
键排序:TreeMap
键插入顺序和取出顺序一致:LinkedHashMap
读取文件:Properties
Collections
工具类
Collections 工具类是一个操作 Set、List 和 Map 等集合的工具类
Collections 中提供了一系列静态的方法对集合元素进行排序、查询和修改等操作
排序操作 均为 static 方法
reverse(List)
:反转 List 中元素的顺序
shuffle(List)
:对 List 集合元素进行随机排序
sort(List)
:根据元素的自然顺序对指定 List 集合元素按升序排序
sort(List, Comparator)
:根据指定的 Comparator 产生的顺序对 List 集合元素进行排序
swap(List, int, int)
:将指定 List 集合中的 i 处元素和 j 处元素进行交换
查找、替换
方法
解释
max(Collection)
根据元素的自然顺序,返回给定集合中的最大元素
max(Collection, Comparator)
根据 Comparator 指定的顺序,返回给定集合中的最大元素
min(Collection)
根据元素的自然顺序,返回给定集合中的最小元素
min(Collection, Comparator)
根据 Comparator 指定的顺序,返回给定集合中的最小元素
frequency(Collection, Object)
返回指定集合中指定元素出现的次数
copy(List dest, List src)
将 src 集合中的内容赋值到 dest 中,需要保证 dest 和 src 的大小相等
replaceAll(List list, Object oldVal, Object new Val)
将 list 集合中的所有 oldVal 全部替换为 newVal
泛型
泛型又称参数化类型,是 JDK 5.0出现的新特性,解决数据类型的安全性问题
在类声明或实例化时只要制定好需要的具体的类型即可
基本语法:ArrayList<类名> list = new ArrayList<类名>();
Java 泛型可以保证如果程序在编译时没有发出警告,运行时就不会产生类型转换异常,代码更加简洁,健壮
泛型的作用是:可以在类声明时通过一个标识表示类中某个属性的类型,或者是某个方法的返回值的类型,或者是参数类型。
优点
编译时,检查添加元素的类型,提高了安全性
减少了类型转换的次数,提高效率
在遍历的时候,可以直接取出对应类的类型,而不是 Object 类型
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 package com.xzt.generic;public class Generic02 { public static void main (String[] args) { Person<String> person = new Person <String>("xzt" ); Person<Integer> person1 = new Person <Integer>(122 ); } } class Person <E> { E s; public Person (E s) { this .s = s; } public E f () { return s; } }
语法 泛型的声明
interface 接口名<T>{}
和 class 类名<K, V>{}
说明:
其中,T,K,V 不代表值,而是表示类型
任意字母都可以,常用T表示,是 Type 的缩写
泛型的实例化
要在类名后米娜指定类型参数的值(类型)。如:
Person<String> person = new Person<String>("xzt");
Iterator<Customer> iterator = cunstomers.iterator();
注意事项
T,K,V只能是引用类型,不能是基本数据类型
在给泛型指定具体类型后,可以传入该类型或者其子类类型
泛型的使用形式
List<String> list = new ArrayList<String>();
List<String> list = new ArrayList<>();
推荐写法,编译器会进行类型推断。
ArrayList list = new ArrayList();
默认泛型是 Object 类
自定义泛型 自定义泛型类 基本语法 :class 类名<T, R ...> { 成员 }
,也可以是接口
注意细节
普通成员可以使用泛型(属性、方法)
使用泛型的数组,不能初始化。因为数组在new的时候,不能确定 T 的类型,就无法在内存中开空间
静态方法中不能使用类的泛型。因为静态方法和类相关,在类加载时,对象还没创建,所以静态方法不能使用泛型
泛型类的类型,是在创建对象时确定的,因为创建对象时,需要指定确定类型
如果在创建对象时,没有指定类型,默认为 Object
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 class Tiger <T, R, M> { String name; R r; T t; M m; T[] ts; public Tiger (String name, R r, T t, M m) { this .name = name; this .r = r; this .t = t; this .m = m; } public String getName () { return name; } public void setName (String name) { this .name = name; } public R getR () { return r; } public void setR (R r) { this .r = r; } public T getT () { return t; } public void setT (T t) { this .t = t; } public M getM () { return m; } public void setM (M m) { this .m = m; } }
自定义泛型接口 基本语法 :interface 接口名<T, R, ...> {}
注意事项
接口中,静态成员也不能使用泛型
泛型接口类型中,在继承接口或者实现接口时确定
没有指定类型,默认为 Object
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 interface IUsb <U, R> { int n = 10 ; R get (U u) ; void hi (R r) ; void Run (R r1, R r2, U u1, U u2) ; default R method (U u) { return null } } interface IA extends IUsb <String, Double>{} class AA implements IA { @Override public Double get (String s) { return null ; } @Override public void hi (Double aDouble) { } @Override public void Run (Double r1, Double r2, String u1, String u2) { } }
自定义泛型方法 基本语法 :修饰符 <T, R, ...>返回类型 方法名(参数列表) {}
注意事项
泛型方法,可以定义在普通类中,也可以定义在泛型类中。
当泛型方法被调用时,类型会确定
若修饰符后面没有泛型<T, R, ..>
,则该方法不是泛型方法
泛型的继承和通配符
泛型不具备继承性。List<Object> list = new ArrayList<String>()
是不正确的
<?>
:支持任意泛型类型
<? extends A>
:支持A类及A类的子类,规定了泛型的上限
<? super A>
:支持A类以及A类的父类,不限于直接父类,规定了泛型的下限
Junit单元测试类
Junit
是一个Java语言的单元测试框架
多数Java的开发环境否已经集成了Junit
作为单元测试工具
在需要测试的方法上面加入@Test
,则可以直接运行该方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 package com.xzt.Junit_;import org.junit.jupiter.api.Test;public class Junit_ { public static void main (String[] args) { } @Test public void m1 () { System.out.println("测试m1方法" ); } @Test public void m2 () { System.out.println("测试m2方法" ); } }
程序进程线程(基础) 基本概念
程序:为完成某个特定任务、用某种语言编写的一组指令的集合。
进程:运行中的程序。是程序的一次执行过程,或是正在运行的程序。
线程:线程是由进程创建的,是进程的一个实体。一个进程可以创建多个线程。
单线程:同一时刻只允许执行一个线程。
多线程:同一时刻可以执行多个线程。
并发:同一时刻,多个任务交替执行。单核cpu实现的多任务就是并发。
并行:同一时刻,多个任务同时执行。多核cpu可以实现并行。
线程使用 两种方法:
继承Thread
类,重写run()
方法
实现Runnable
接口,重写run()
方法
继承Thread
类
当一个类继承了Thread
类,该类就可以当作线程使用
会重写run
方法,写自己的业务代码
run
Thread
类实现了Runnable
接口中的run
方法。
运行流程 ⭐
运行main
函数 => 开启进程 => 创建main
线程 => 创建子线程Thread-0
。
当main
线程启动一个子线程Thread-0
时,主线程不会阻塞,会继续执行。
注意 :需要进程中的所有线程结束后进程才会结束。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 package com.xzt.thread;public class Cat extends Thread { int times = 1 ; @Override public void run () { while (true ) { System.out.println("mmm" + (times ++)); try { Thread.sleep(1000 ); } catch (InterruptedException e) { throw new RuntimeException (e); } if (times == 5 ) break ; } } } package com.xzt.thread;public class Thread01 { public static void main (String[] args) { Cat cat = new Cat (); cat.start(); System.out.println("主线程继续执行:" + Thread.currentThread().getName()); for (int i = 0 ; i < 60 ; i++) { System.out.println("主线程:i = " + i); try { Thread.sleep(1000 ); } catch (InterruptedException e) { throw new RuntimeException (e); } } } }
start()
函数源码
1 2 3 4 5 6 7 8 9 start(); (1 ) public synchronized void start () { start0(); } (2 ) private native void start0 () ;
实现Runnable
接口💡
java是单继承的,在某些情况下一个类已经继承了某个父类,这时在用继承Thread
类方法来创建线程则不能了。
则可以通过实现Runnable
接口来创建线程。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 package com.xzt.thread;public class Runnable01 { public static void main (String[] args) { Dog dog = new Dog (); Thread thread = new Thread (dog); thread.start(); } } class Dog implements Runnable { int count = 0 ; @Override public void run () { while (true ) { System.out.println("小狗汪汪叫.." + (++ count) + Thread.currentThread().getName()); try { Thread.sleep(1000 ); } catch (InterruptedException e) { throw new RuntimeException (e); } if (count == 10 ) break ; } } }
模拟Thread
类的静态代理模式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 package com.xzt.thread;public class Thread02 { public static void main (String[] args) { Tiger tiger = new Tiger (); ThreadProxy threadProxy = new ThreadProxy (tiger); threadProxy.start(); } } class Aniaml {}class Tiger extends Aniaml implements Runnable { @Override public void run () { System.out.println("老虎嗷嗷叫.." ); } } class ThreadProxy implements Runnable { private Runnable target = null ; @Override public void run () { if (target != null ) { target.run(); } } public ThreadProxy (Runnable target) { this .target = target; } public void start () { start0(); } public void start0 () { run(); } }
Thread
和Runnale
的区别
没有本质区别,从jdk文档可以看到Thread
类本身就实现了Runnale
接口start() -> start0()
实现Runnable
接口更加适合多个线程共享 一个资源的情况,并且避免了单继承的限制。建议使用
多线程 创建两个线程,分别执行不同的功能。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 package com.xzt.thread;public class Thread03 { public static void main (String[] args) { T1 t1 = new T1 (); T2 t2 = new T2 (); Thread thread1 = new Thread (t1); Thread thread2 = new Thread (t2); thread1.start(); thread2.start(); } } class T1 implements Runnable { private int count = 0 ; @Override public void run () { while (true ) { System.out.println("Hello World " + (++count)); try { Thread.sleep(1000 ); } catch (InterruptedException e) { throw new RuntimeException (e); } if (count == 10 ) break ; } } } class T2 implements Runnable { private int count = 0 ; @Override public void run () { while (true ) { System.out.println("Hi.. " + (++count)); try { Thread.sleep(1000 ); } catch (InterruptedException e) { throw new RuntimeException (e); } if (count == 5 ) break ; } } }
线程终止
线程完成任务后,会自动退出
还可以通过使用变量来控制run方法退出的方式停止线程,即通知方式。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 package com.xzt.exit_;public class ThreadExit_ { public static void main (String[] args) { T t1 = new T (); t1.start(); try { Thread.sleep(10000 ); } catch (InterruptedException e) { throw new RuntimeException (e); } t1.setLoop(false ); } } class T extends Thread { private int count = 0 ; private boolean loop = true ; @Override public void run () { while (loop) { try { Thread.sleep(50 ); } catch (InterruptedException e) { throw new RuntimeException (e); } System.out.println("T 运行中..." + (++ count)); } } public void setLoop (boolean loop) { this .loop = loop; } }
线程的常用方法
方法
含义
setName(name)
设置线程名称,使之与参数name相同
getName(name)
返回该线程的名称
start()
启动线程,java虚拟机底层调用start0
方法
run()
调用线程对象run
方法
setPriority(idx)
更改线程的优先级
getPriority()
获取线程的优先级
sleep(time)
在指定的毫秒数内让当前正在执行的线程休眠
interrupt
中断线程,注意不是终止
yield
线程的礼让,让出一下cpu让其他线程执行。但礼让时间不确定。所以也不一定成功
join
线程的插队,插队的线程一旦插队成功,则肯定先执行玩插入的线程所有任务
用户线程:也叫工作线程,当线程的任务执行完或通知方式结束
守护线程:一般是为工作线程服务的,当所有的用户线程结束,守护线程自动结束。
常见的守护线程:垃圾回收机制。
设置守护线程的方法:子线程.setDaemon(true)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 package com.hspedu.method;public class ThreadMethod03 { public static void main (String[] args) throws InterruptedException { MyDaemonThread myDaemonThread = new MyDaemonThread (); myDaemonThread.setDaemon(true ); myDaemonThread.start(); for ( int i = 1 ; i <= 10 ; i++) { System.out.println("宝强在辛苦的工作..." ); Thread.sleep(1000 ); } } } class MyDaemonThread extends Thread { public void run () { for (; ; ) { try { Thread.sleep(1000 ); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("马蓉和宋喆快乐聊天,哈哈哈~~~" ); } } }
线程的生命周期(状态)
状态转移图
锁 Synchronized
线程同步 ⭐synchronized
所得是对象不是代码,锁方法和非锁方法同时执行。
在多线程编程,一些敏感词数据不允许被多个线程同时访问,此时就是用同步访问技术,保证数据在任何时刻,最多有一个线程访问,以保证数据的完整性。
线程同步,即当有一个线程在对内存进行操作时,其他线程都不能对这个内存地址进行操作,直到该线程完成操作,其他线程才能对该内存地址进行操作。
属性
可重入 ,可以加很多道锁,但是锁的还是this
异常锁,若程序产生了异常,则会自动释放锁。
synchronized
底层实现
当synchronized(object)
后,会首先使用 markword 记录这个线程ID(偏向锁);然后如果产生了线程争用,则升级为自旋锁;10次以后,升级为重量级锁 - OS
执行时间短(加锁代码),线程数少,用自旋
执行时间长,线程数多,用系统锁
使用方法
synchronized
还可以放在方法声明中,表示整个方法为同步方法。
1 2 3 public synchronized void m (String name) { }
实例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 package com.hspedu.syn;public class SellTicket { public static void main (String[] args) { SellTicket03 sellTicket03 = new SellTicket03 (); new Thread (sellTicket03).start(); new Thread (sellTicket03).start(); new Thread (sellTicket03).start(); } } class SellTicket03 implements Runnable { private int ticketNum = 100 ; private boolean loop = true ; Object object = new Object (); public synchronized static void m1 () { } public static void m2 () { synchronized (SellTicket03.class) { System.out.println("m2" ); } } public void sell () { synchronized ( object) { if (ticketNum <= 0 ) { System.out.println("售票结束..." ); loop = false ; return ; } System.out.println("窗口 " + Thread.currentThread().getName() + " 售出一张票" + " 剩余票数=" + (--ticketNum)); } } @Override public void run () { while (loop) { try { Thread.sleep(50 ); } catch (InterruptedException e) { e.printStackTrace(); } sell(); } } }
互斥锁
java语言中,引入了对象互斥锁的概念,来保证共享数据的完整性。
关键字synchronized
来与对象的互斥锁联系,当某对象用synchronized 修饰时,表明该对象在任意时刻只能由一个线程访问。
同步方法(非静态)的锁可以是this,也可以是其他对象(要求是同一对象)
同步方法(静态static
)的锁可以是当前类本身。类.class
死锁
多个线程都占用了对方的锁资源,但不肯相让,导致了死锁。在编程时一定要避免死锁的放生。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 package com.hspedu.syn;public class DeadLock_ { public static void main (String[] args) { DeadLockDemo A = new DeadLockDemo (true ); A.setName("A线程" ); DeadLockDemo B = new DeadLockDemo (false ); B.setName("B线程" ); A.start(); B.start(); } } class DeadLockDemo extends Thread { static Object o1 = new Object (); static Object o2 = new Object (); boolean flag; public DeadLockDemo (boolean flag) { this .flag = flag; } @Override public void run () { if (flag) { synchronized (o1) { System.out.println(Thread.currentThread().getName() + " 进入1" ); synchronized (o2) { System.out.println(Thread.currentThread().getName() + " 进入2" ); } } } else { synchronized (o2) { System.out.println(Thread.currentThread().getName() + " 进入3" ); synchronized (o1) { System.out.println(Thread.currentThread().getName() + " 进入4" ); } } } } }
释放锁
当前线程的同步方法、同步代码块执行结束
当前线程在同步代码块、同步方法中遇到break
、return
当前线程在同步代码块 、同步方法中出现了未处理的Error
或Exception
,导致异常结束
当前线程在同步代码块、同步方法中执行了线程对象的wait()
方法,当前线程暂停,并释放锁
synchronized
优化
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 package com.xzt.test;public class FineCoarseLock { private int count = 0 ; public synchronized void m1 () { try { Thread.sleep(1000 ); } catch (InterruptedException e) { throw new RuntimeException (e); } count ++; try { Thread.sleep(1000 ); } catch (InterruptedException e) { throw new RuntimeException (e); } } public void m2 () { try { Thread.sleep(1000 ); } catch (InterruptedException e) { throw new RuntimeException (e); } synchronized (this ) { count++; } try { Thread.sleep(1000 ); } catch (InterruptedException e) { throw new RuntimeException (e); } } }
volatile
保证线程可见性,每次改写都会被线程读取。
禁止指令重排序
不能保证原子性,并不能保证多个线程共同修改running变量时所带来的不一致问题。
实例1:保证线程可见性
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 package com.xzt.test;import sun.awt.windows.ThemeReader;public class T { public volatile boolean flag = true ; public void m () { System.out.println("m start" ); while (flag) { } System.out.println("m end!" ); } public static void main (String[] args) { T t = new T (); new Thread (t::m, "t1" ).start(); try { Thread.sleep(1000 ); } catch (InterruptedException e) { throw new RuntimeException (e); } t.flag = false ; } }
实例2:禁止指令重排序
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 package com.xzt.test;public class Mgr06 { private static volatile Mgr06 INSTANCE; private Mgr06 () { } public static Mgr06 getINSTANCE () { if (INSTANCE == null ) { synchronized (Mgr06.class) { if (INSTANCE == null ) { try { Thread.sleep(1 ); } catch (InterruptedException e) { throw new RuntimeException (e); } INSTANCE = new Mgr06 (); } } } return INSTANCE; } public static void main (String[] args) { for (int i = 0 ; i < 100 ; i++) { new Thread (() -> { System.out.println(Mgr06.getINSTANCE().hashCode()); }).start(); } } }
多线程与高并发⭐⭐⭐ 凡是从时间角度或者优先级角度解决多线程问题皆不对!!!凡是从join
考虑的,也全不对!!!
哲学家就餐问题
死锁写法
1 2 3 4 5 6 7 8 9 10 package com.xzt.PhilosopherDining;public class ChopStick {}
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 package com.xzt.PhilosopherDining;public class T01_DeadLock { public static void main (String[] args) { ChopStick ch0 = new ChopStick (); ChopStick ch1 = new ChopStick (); ChopStick ch2 = new ChopStick (); ChopStick ch3 = new ChopStick (); ChopStick ch4 = new ChopStick (); Philosohper p0 = new Philosohper ("p0" , 0 , ch0, ch1); Philosohper p1 = new Philosohper ("p1" , 1 , ch1, ch2); Philosohper p2 = new Philosohper ("p2" , 2 , ch2, ch3); Philosohper p3 = new Philosohper ("p3" , 3 , ch3, ch4); Philosohper p4 = new Philosohper ("p4" , 4 , ch4, ch0); p0.start(); p1.start(); p2.start(); p3.start(); p4.start(); } public static class Philosohper extends Thread { private ChopStick left, right; private int index; public Philosohper (String name, int index, ChopStick left, ChopStick right) { this .setName(name); this .index = index; this .left = left; this .right = right; } @Override public void run () { synchronized (right) { try { Thread.sleep(1000 ); } catch (InterruptedException e) { throw new RuntimeException (e); } synchronized (left) { try { Thread.sleep(1000 ); } catch (InterruptedException e) { throw new RuntimeException (e); } System.out.println(index + " 号哲学家已经吃完" ); } } } } }
左撇子算法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 package com.xzt.PhilosopherDining;public class T02_DeadLockOpen { public static void main (String[] args) { ChopStick ch0 = new ChopStick (); ChopStick ch1 = new ChopStick (); ChopStick ch2 = new ChopStick (); ChopStick ch3 = new ChopStick (); ChopStick ch4 = new ChopStick (); Philosohper p0 = new Philosohper ("p0" , 0 , ch0, ch1); Philosohper p1 = new Philosohper ("p1" , 1 , ch1, ch2); Philosohper p2 = new Philosohper ("p2" , 2 , ch2, ch3); Philosohper p3 = new Philosohper ("p3" , 3 , ch3, ch4); Philosohper p4 = new Philosohper ("p4" , 4 , ch4, ch0); p0.start(); p1.start(); p2.start(); p3.start(); p4.start(); } public static class Philosohper extends Thread { private ChopStick left, right; private int index; public Philosohper (String name, int index, ChopStick left, ChopStick right) { this .setName(name); this .index = index; this .left = left; this .right = right; } @Override public void run () { if (index % 2 == 0 ) { synchronized (left) { try { Thread.sleep(1000 ); } catch (InterruptedException e) { throw new RuntimeException (e); } synchronized (right) { try { Thread.sleep(1000 ); } catch (InterruptedException e) { throw new RuntimeException (e); } System.out.println(index + " 号哲学家已经吃完" ); } } } else { synchronized (right) { try { Thread.sleep(1000 ); } catch (InterruptedException e) { throw new RuntimeException (e); } synchronized (left) { try { Thread.sleep(1000 ); } catch (InterruptedException e) { throw new RuntimeException (e); } System.out.println(index + " 号哲学家已经吃完" ); } } } } } }
交替输出 两个字符串1234567
,ABCDEFG
,要求交替输出,并且每次先输出数字。
实现方式1:LockSupport LockSupport.unpark(t1)
、LockSupport.park()
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 package com.xzt.alternateoutput;import java.util.concurrent.locks.Lock;import java.util.concurrent.locks.LockSupport;public class T01_Output { static Thread t1 = null , t2 = null ; public static void main (String[] args) { char [] aI = "1234567" .toCharArray(); char [] aC = "ABCDEFG" .toCharArray(); t1 = new Thread (() -> { for (char c : aI) { System.out.println(c); LockSupport.unpark(t2); LockSupport.park(); } }); t2 = new Thread (() -> { for (char c : aC) { LockSupport.park(); System.out.println(c); LockSupport.unpark(t1); } }); t1.start(); t2.start(); } }
实现方法2synchronized
wait(); notify()
存在问题:可能先输出了字母后输出了数字。可以使用CountDownLatch latch = new CountDownLatch(1); // 门闩
给后输出的前面插上门闩latch.await()
,等先输出的输出结束后放下门闩latch.countDown()
,如下注释部分
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 package com.xzt.alternateoutput;import java.util.concurrent.CountDownLatch;public class T02_WaitNotify { static Thread t1 = null , t2 = null ; public static void main (String[] args) { Object o = new Object (); char [] aI = "1234567" .toCharArray(); char [] aC = "ABCDEFG" .toCharArray(); t1 = new Thread (() -> { synchronized (o) { for (char c : aI) { System.out.println(c); try { o.notify(); o.wait(); } catch (InterruptedException e) { throw new RuntimeException (e); } } o.notify(); } }); t2 = new Thread (() -> { synchronized (o) { for (char c : aC) { System.out.println(c); try { o.notify(); o.wait(); } catch (InterruptedException e) { throw new RuntimeException (e); } } o.notify(); } }); t1.start(); t2.start(); } }
实现方式3:RenntrantLock()
RenntrantLock()
可重入锁,可以做队列。singal(); await();
RenntrantLock
和synchronized
的区别
RenntrantLock
可以做队列,多个队列Condition
;synchronized
只有一个队列
RenntrantLock
可以做公平锁;synchronized
只能做非公平锁。
RenntrantLock
可以尝试上锁;synchronized
只能傻傻等待。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 package com.xzt.alternateoutput;import java.util.concurrent.CountDownLatch;import java.util.concurrent.locks.Condition;import java.util.concurrent.locks.Lock;import java.util.concurrent.locks.ReentrantLock;public class T03_Lock_Condition { public static void main (String[] args) { char [] aI = "1234567" .toCharArray(); char [] aC = "ABCDEFG" .toCharArray(); Lock lock = new ReentrantLock (); Condition condition1 = lock.newCondition(); Condition condition2 = lock.newCondition(); CountDownLatch latch = new CountDownLatch (1 ); new Thread (() -> { try { latch.await(); } catch (InterruptedException e) { throw new RuntimeException (e); } lock.lock(); try { for (char c : aI) { System.out.println(c); condition2.signal(); condition1.await(); } condition2.signal(); } catch (Exception e) { e.printStackTrace(); }finally { lock.unlock(); } }, "t1" ).start(); new Thread (() -> { lock.lock(); try { for (char c : aC) { System.out.println(c); latch.countDown(); condition1.signal(); condition2.await(); } condition1.signal(); } catch (Exception e) { e.printStackTrace(); }finally { lock.unlock(); } }, "t2" ).start(); } }
实现方式4:TransferQueue<>
TransferQueue<>
是一个交换队列,容量为0,只能用于交换。互相打印对方的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 package com.xzt.alternateoutput;import java.util.concurrent.LinkedTransferQueue;public class T04_TransferQueue { public static void main (String[] args) { char [] aI = "1234567" .toCharArray(); char [] aC = "ABCDEFG" .toCharArray(); LinkedTransferQueue<Character> queue = new LinkedTransferQueue <>(); new Thread (() -> { try { for (char c : aI) { queue.transfer(c); System.out.println(queue.take()); } } catch (Exception e) { e.printStackTrace(); } }, "t1" ).start(); new Thread (() -> { try { for (char c : aC) { System.out.println(queue.take()); queue.transfer(c); } } catch (Exception e) { e.printStackTrace(); } }, "t1" ).start(); } }
事务问题 三个任务,一个任务执行错误,其他任务都应该取消。
CAS 无锁优化,自旋 AtomicInterge
不需要加锁,
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 package com.xzt.thread;import java.util.ArrayList;import java.util.List;import java.util.concurrent.atomic.AtomicInteger;public class T01_AtomicInterge { AtomicInteger count = new AtomicInteger (0 ); public void m () { for (int i = 0 ; i < 10000 ; i++) { count.incrementAndGet(); } } public static void main (String[] args) { T01_AtomicInterge t = new T01_AtomicInterge (); List<Thread> threads = new ArrayList <>(); for (int i = 0 ; i < 10 ; i++) { threads.add(new Thread (t::m, "thread-" + i)); } threads.forEach((o) -> o.start()); threads.forEach((o) -> { try { o.join(); } catch (InterruptedException e) { throw new RuntimeException (e); } }); System.out.println(t.count); } }
新类型锁(同步锁) synchronized
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 package com.xzt.thread;public class T00_Synchronized { public synchronized void m1 () { for (int i = 0 ; i < 10 ; i++) { try { Thread.sleep(1000 ); } catch (InterruptedException e) { throw new RuntimeException (e); } System.out.println(i); } } public synchronized void m2 () { System.out.println("m2......" ); } public static void main (String[] args) { T00_Synchronized t = new T00_Synchronized (); new Thread (t::m1, "t1" ).start(); try { Thread.sleep(1000 ); } catch (InterruptedException e) { throw new RuntimeException (e); } new Thread (t::m2, "t2" ).start(); } }
RenntrantLock
可重入锁private Lock lock = new ReentrantLock();
优点
使用 try lock进行尝试锁定,不管锁定与否,方法都将继续执行。下方m2方法
公平锁 ,默认是非公平锁,公平锁定义方法private ReentrantLocklock = new ReentrantLock(true);
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 package com.xzt.thread;import java.util.concurrent.TimeUnit;import java.util.concurrent.locks.Lock;import java.util.concurrent.locks.ReentrantLock;public class T01_RenntrantLock { private Lock lock = new ReentrantLock (); public void m1 () { lock.lock(); try { for (int i = 0 ; i < 10 ; i++) { Thread.sleep(1000 ); System.out.println(i); } } catch (Exception e) { e.printStackTrace(); } finally { lock.unlock(); } } public void m2 () { boolean locked = false ; try { locked = lock.tryLock(5 , TimeUnit.SECONDS); System.out.println("m2 ...." ); } catch (InterruptedException e) { throw new RuntimeException (e); } finally { if (locked) lock.unlock(); } } public static void main (String[] args) { T01_RenntrantLock t = new T01_RenntrantLock (); new Thread (t::m1, "t1" ).start(); try { Thread.sleep(1000 ); } catch (InterruptedException e) { throw new RuntimeException (e); } new Thread (t::m2, "t2" ).start(); } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 package com.xzt.thread;import java.util.concurrent.locks.ReentrantLock;public class T02_RenntrantLock2 extends Thread { private static ReentrantLock lock = new ReentrantLock (true ); @Override public void run () { for (int i = 0 ; i < 100 ; i++) { lock.lock(); try { System.out.println(Thread.currentThread().getName() + "获得锁" ); } finally { lock.unlock(); } } } public static void main (String[] args) { T02_RenntrantLock2 t = new T02_RenntrantLock2 (); new Thread (t, "t1" ).start(); new Thread (t, "t2" ).start(); } }
CountDownLatch
门闩CountDownLatch latch = new CountDownLatch(值);
最终要的方法
latch.countDown();
门闩值减一。
latch.await();
插上门闩,等待门闩的值减为0后继续往后执行。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 package com.xzt.thread;import java.util.concurrent.CountDownLatch;public class T03_CountDownLatchTest { private static void usingCountDownLatch () { Thread[] threads = new Thread [100 ]; CountDownLatch latch = new CountDownLatch (threads.length); for (int i = 0 ; i < threads.length; i++) { threads[i] = new Thread (() -> { int result = 0 ; for (int j = 0 ; j < 10000 ; j++) { result += j; } latch.countDown(); }); } for (int i = 0 ; i < threads.length; i++) { threads[i].start(); } try { latch.await(); } catch (InterruptedException e) { throw new RuntimeException (e); } System.out.println("end Latch" ); } private static void usingJoin () { Thread[] threads = new Thread [100 ]; for (int i = 0 ; i < threads.length; i++) { threads[i] = new Thread (() -> { int result = 0 ; for (int j = 0 ; j < 10000 ; j++) { result += j; } }); } for (int i = 0 ; i < threads.length; i++) { threads[i].start(); } for (int i = 0 ; i < threads.length; i++) { try { threads[i].join(); } catch (InterruptedException e) { throw new RuntimeException (e); } } System.out.println("end Latch" ); } public static void main (String[] args) { usingCountDownLatch(); } }
CyclicBarrier
栅栏主要用于限流。
定义方法
1 2 3 4 5 6 7 8 9 10 CyclicBarrier barrier = new CyclicBarrier (20 , new Runnable () { @Override public void run () { System.out.println("满人,发车!!!" ); } }); CyclicBarrier barrier1 = new CyclicBarrier (20 , () -> { System.out.println("满人,发车" ); });
实例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 package com.xzt.thread;import java.util.concurrent.CyclicBarrier;public class T04_TestCyclicBarrier { public static void main (String[] args) { CyclicBarrier barrier = new CyclicBarrier (20 , new Runnable () { @Override public void run () { System.out.println("满人,发车!!!" ); } }); CyclicBarrier barrier1 = new CyclicBarrier (20 , () -> { System.out.println("满人,发车" ); }); for (int i = 0 ; i < 100 ; i++) { new Thread (() -> { try { barrier.await(); } catch (Exception e){ e.printStackTrace(); } }).start(); } } }
Phaser
阶段…
ReadWriteLock
读写锁
读锁是共享锁,写锁是独占锁。读锁共享锁可保证并发读是非常高效的,其中读写,写读,写写是互斥的
synchronized
和renntrantLock
都是独占锁。
定义方法
1 2 3 4 static ReadWriteLock readWriteLock = new ReentrantReadWriteLock (); static Lock readLock = readWriteLock.readLock(); static Lock writeLock = readWriteLock.writeLock();
实例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 package com.xzt.thread;import javax.sound.sampled.FloatControl;import java.util.Random;import java.util.concurrent.locks.Lock;import java.util.concurrent.locks.ReadWriteLock;import java.util.concurrent.locks.ReentrantLock;import java.util.concurrent.locks.ReentrantReadWriteLock;public class T05_TestReadWirteLock { static Lock lock = new ReentrantLock (); private static int value; static ReadWriteLock readWriteLock = new ReentrantReadWriteLock (); static Lock readLock = readWriteLock.readLock(); static Lock writeLock = readWriteLock.writeLock(); public static void read (Lock lock) { try { lock.lock(); Thread.sleep(1000 ); System.out.println("read over!!" ); } catch (Exception e) { e.printStackTrace(); } finally { lock.unlock(); } } public static void write (Lock lock, int v) { try { lock.lock(); Thread.sleep(1000 ); value = v; System.out.println("write over!!" ); } catch (Exception e) { e.printStackTrace(); } finally { lock.unlock(); } } public static void main (String[] args) { Runnable readR = () -> read(readLock); Runnable writeR = () -> write(writeLock, new Random ().nextInt()); for (int i = 0 ; i < 18 ; i++) { new Thread (readR).start(); } for (int i = 0 ; i < 2 ; i++) { new Thread (writeR).start(); } } }
Semaphore
信号量定义方法 :Semaphore s = new Semaphore(值, true)
,此处的 值 代表最大允许同时执行的线程个数。true
代表公平锁。
常用方法
s.acquire()
获得信号量(锁),信号量值减一,若为0,则其他线程无法获取
s.release()
释放信号量(锁),信号量值加一。
实例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 package com.xzt.thread;import java.util.concurrent.Semaphore;public class T06_TestSemaphore { public static void main (String[] args) { Semaphore semaphore = new Semaphore (1 ); new Thread (() -> { try { semaphore.acquire(); System.out.println("T1 running...." ); Thread.sleep(1000 ); System.out.println("T1 running...." ); semaphore.release(); } catch (Exception e) { e.printStackTrace(); } }, "t1" ).start(); new Thread (() -> { try { semaphore.acquire(); System.out.println("T2 running...." ); Thread.sleep(1000 ); System.out.println("T2 running...." ); semaphore.release(); } catch (Exception e) { e.printStackTrace(); } }, "t2" ).start(); } }
Exchanger
交换器static Exchanger<String> exchanger = new Exchanger<>();
交换两个线程中的内容,只能是两个线程之间。
实例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 package com.xzt.thread;import java.util.concurrent.Exchanger;public class T07_TestExchanger { static Exchanger<String> exchanger = new Exchanger <>(); public static void main (String[] args) { new Thread (() -> { String s = "T1" ; try { s = exchanger.exchange(s); } catch (InterruptedException e) { throw new RuntimeException (e); } System.out.println(Thread.currentThread().getName() + " " + s); }, "t1" ).start(); new Thread (() -> { String s = "T2" ; try { s = exchanger.exchange(s); } catch (InterruptedException e) { throw new RuntimeException (e); } System.out.println(Thread.currentThread().getName() + " " + s); }, "t2" ).start(); } }
LockSupport
可以任意位置停止。并且可以叫醒指定线程。
常用方法
LockSupport.park()
,将线程暂停,但不是终止。
LockSupport.unpark(线程名)
,将指定线程恢复。并且unpark
可以在park
之前调用
实例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 package com.xzt.thread;import java.util.concurrent.locks.LockSupport;public class T08_TestLockSupport { public static void main (String[] args) { Thread t = new Thread (() -> { for (int i = 0 ; i < 10 ; i++) { System.out.println(i); if (i == 5 ) LockSupport.park(); try { Thread.sleep(1000 ); } catch (InterruptedException e) { throw new RuntimeException (e); } } }); t.start(); try { Thread.sleep(8000 ); } catch (InterruptedException e) { throw new RuntimeException (e); } System.out.println("8s end!!!" ); LockSupport.unpark(t); } }
IO流 文件
文件是保存数据的地方
文件流
文件在程序中是以流的形式来操作的
输入流:数据从数据源(文件)到程序(内存)的路径
输出流:数据从程序(内存)到数据源(文件)的路径
常用的文件操作
1 2 3 4 5 File file = new File (String pathname); File file = new File (File parent, String child); File file = new File (String parent, String child); file.crateNewFile();
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 @Test public void info () { File file = new File ("e:\\news1.txt" ); System.out.println("文件名字=" + file.getName()); System.out.println("文件绝对路径=" + file.getAbsolutePath()); System.out.println("文件父级目录=" + file.getParent()); System.out.println("文件大小(字节)=" + file.length()); System.out.println("文件是否存在=" + file.exists()); System.out.println("是不是一个文件=" + file.isFile()); System.out.println("是不是一个目录=" + file.isDirectory()); }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 @Test public void m1 () { String filePath = "e:\\news1.txt" ; File file = new File (filePath); if (file.exists()) { if (file.delete()) { System.out.println(filePath + "删除成功" ); } else { System.out.println(filePath + "删除失败" ); } } else { System.out.println("该文件不存在..." ); } } @Test public void m2 () { String filePath = "D:\\demo02" ; File file = new File (filePath); if (file.exists()) { if (file.delete()) { System.out.println(filePath + "删除成功" ); } else { System.out.println(filePath + "删除失败" ); } } else { System.out.println("该目录不存在..." ); } } @Test public void m3 () { String directoryPath = "D:\\demo\\a\\b\\c" ; File file = new File (directoryPath); if (file.exists()) { System.out.println(directoryPath + "存在.." ); } else { if (file.mkdirs()) { System.out.println(directoryPath + "创建成功.." ); } else { System.out.println(directoryPath + "创建失败..." ); } } }
IO流原理及流的分类
I/O技术是非常实用的技术,用于处理数据传输。如读/写文件。网络通讯等。
java程序中,对于数据的输入和输出是以stream
流的方式进行
java.io
包下提供了各种流类和接口。
I/O流原理
输入input:读取外部数据到程序(内存)中
输出output:将程序输出到磁盘、光盘等存储设备中。
流的分类
615
节点流和处理流 输入流 输出流 Properties
类