JAVA基础速通(适合有C++基础的看)

IDEA操作

IDEA导入Eclipese文件

在这里插入图片描述
2.选择文件

在这里插入图片描述

3.选择Eclipse
在这里插入图片描述

无脑下一步

4.更改项目结构选择jdk

在这里插入图片描述

导出可运行Jar包

打开项目结构,选择以下

在这里插入图片描述

选择主类

在这里插入图片描述

构建

在这里插入图片描述

构建

在这里插入图片描述
运行jar包

在这里插入图片描述

导入JAR包

在这里插入图片描述

之后选择jar包路径即可

数据类型

在这里插入图片描述

面向对象编程

类的五大成员:方法,属性,代码块,内部类,构造器

面向对象编程—多态

向上转型和向下转型

//向上转型
class A{

}


class B extends A{

}

public class Example{
    public static void main(String[] args){
        A a = new B(); 
        //向上转型 如果A调用方法,这个方法要先从B中找
        //等号前面为引用(编译),等号后面为对象(运行)  
        //调用属性看引用,调用方法看对象
    }
}
//向下转型
class A{
}

class B extends A{

}

public class Example{
    public static void main(String[] args){
        A a = new B();
        B b = (B) a; //父类的引用必须指向当前目标类型的对象
                     //即引用a必须指向B的对象
    }
}

动态绑定机制(非常重要)

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

调用对象属性时,没有动态绑定机制

class A{
    public int i = 10;
    public int sum(){
        return getI() + 10;
    }
    public int sum1(){
        return i + 10;
    }
    public int getI(){
    return i;
    }
}
class B extends A{
    public int i = 20;
    public int sum(){
        return i + 20;
    }
    public int sum1(){
        return i + 10;
    }
    public int getI(){
        return i;
    }
}
public class Example{
    public static void main(String[] args){
        A a = new B();//向上转型
        System.out.println(a.sum()); //40
        System.out.println(a.sum1()); //30
    }
}
class A{
    public int i = 10;
    public int sum(){
        return getI() + 10;
    }
    public int sum1(){
        return i + 10;
    }
    public int getI(){
        return i;
    }
}
class B extends A{
    public int i = 20;
//    public int sum(){
//        return i + 20;
//    }
//    public int sum1(){
//        return i + 10;
//    }
    public int getI(){
        return i;
    }
}
public class Example{
    public static void main(String[] args){
        A a = new B();//向上转型
        System.out.println(a.sum()); //40-》30
        System.out.println(a.sum1()); //30-》20
//a动态绑定为B, A中sum方法返回getI()+10,getI()是B中的getI()
//A中sum1()方法返回i+10,而这个i没有动态绑定机制,就是A中的i
    }
}

多态应用—多态数组

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

class Person{
    private String name = "李华";
    private int age;
    public Person() {

    }
    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }
    public String say() {
        return "我叫"+name+"年龄为"+age;
    }
}

class Student extends Person{
    private  int id;
    private int score;
    public Student(String name,int age, int id, int score) {
        // TODO Auto-generated constructor stub
        super(name, age);
        this.id = id;
        this.score = score;
    }
    public String say() {    
        return super.say()+"学号"+id+"成绩"+score;
    }
}

class Teacher extends Person{
    int id;
    int salary;
    public Teacher(String name,int age, int id, int salary) {
        // TODO Auto-generated constructor stub
        super(name, age);
        this.id = id;
        this.salary = salary;
    }
    public String say() {
        return super.say()+"编号"+id+"薪水"+salary;
    }
}
/* 运行结果:
我叫smith年龄为18
我叫Black年龄为30编号2000薪水25000
我叫Green年龄为50编号1200薪水20000
我叫liu年龄为15学号1成绩60
我叫liuxiao年龄为14学号2成绩89
*/


//如想用特有属性则在for循环中使用向下转型即:
for(int i = 0; i<persons.length;i++) {
            System.out.println(persons[i].say());
            if(persons[i] instanceof Student) {
                Student student = (Student)persons[i];
                student.study();
//                ((Student)persons[i]).study();
            }else if(persons[i] instanceof Teacher) {
                Teacher teacher = (Teacher)persons[i];
                teacher.teach();
            }
        }

多态参数

方法定义的形参为父类,传的实参可以为子类

public class Example{
    public static void main(String[] args) {
        Worker lilan = new Worker(3000, "lilan");
        Manager liqing = new Manager(4500, "liqing", 2000);

        Example example = new Example();
        example.showEmpAnnual(lilan);
        example.showEmpAnnual(liqing);

        example.showWork(lilan);
        example.showWork(liqing);
    }
    public void showEmpAnnual(Employee e) {
        //形参e相当于 Employee的引用,实际指向的是实参的对象
        System.out.println(e.getAnnual());
    }
    public void showWork(Employee e) {
        if(e instanceof Worker){
            ((Worker)e).work();//向下转型
        }else if(e instanceof Manager) {
            ((Manager)e).manage();//向下转型
        }
    }
}


/*
36000
56000
普通员工lilanis working
经理liqingis managing

object类的详解

==运算符

1.==是一个比较运算符

    可以判断基本类型,也可判断引用类型

    判断基本类型时,判断值是否相等

    判断引用类型时,判断的是地址是否相等
class Example{
    public static void main(String[] args){
        A a = new A();
        A b = a;
        A c = b;
        B bobj = a;//向上转型
        System.out.println(a==b)//true 比较的是地址
        System.out.println(b==c)//true
        System.out.println(bobj==a)//true
    }
}

class B {}
class A extends A{}

equals

equals只能判断引用类型

object子类往往重写equals方法用来判断值是否相等

hashCode

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

2.两个引用指向相同的对象哈希值是一样的

3.哈希值主要是根据地址号来处理的,不能完全将哈希值等价于地址

toString

返回该对象的的字符串表示,默认返回:全类名+@+哈希值的十六进制(全类名:包名+类名)

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

package 方法重写;

public class Example{
    public static void main(String[] args) {
        Monster monster = new Monster("小狐狸", 1000);
        System.out.println(monster);
    }
}

class Monster{
    private String name;
    private int salary;
    public Monster(String name, int salary) {
        // TODO Auto-generated constructor stub
        this.name = name;
        this.salary = salary;
    }
}
/*方法重写.Monster@27bc2616

经常重写该方法使其输出对象

class Monster{
    private String name;
    private int salary;
    public Monster(String name, int salary) {
        // TODO Auto-generated constructor stub
        this.name = name;
        this.salary = salary;
    }
    public String toString() {
        return "Monster"+name+salary;
    }

}

finalize

1.当垃圾回收器确定不存在该对象的更多引用时,由对象被回收时使用

  1. 对象被回收前,系统自动调用该方法,子类可以重写该方法,实现自己的业务(释放资源,数据库连接,或者打开文件
  2. 垃圾回收机制的调用由系统决定,也可以使用System.gc()手动回收

main方法语法

  1. main方法是虚拟机调用
  2. java虚拟机需要调用类的main(方法,所以该方法的访问权限必须是public
  3. java虚拟机在执行main()方法时不必创建对象,所以该方法必须是static
  4. 该方法接收String类型的数组参数,该数组中保存执行java命令时传递给所运行的类的参数
  5. java执行的程序 参数1 参数2 参数3

特别提示:

  1. main()方法中,我们可以直接调用main()方法所在类的静态方法和静态成员
  2. 不能调用非静态方法和非静态成员,如果需要调用则需要创建对象

动态传参:

在下面图片程序实参中传入参数

在这里插入图片描述

代码块

介绍

相当于对构造器的补充,可以做初始化操作,如果多个构造器有重复的语句,可以抽取到初始化块中,提高代码重用性

代码块优先于构造器

public class A {
    public static void main(String[] args) {
        B b = new B("小梦");
        B b1 = new B("小红",14);
        B b2 = new B("小兰",20,1000);
    }
}

class B{
    String name;
    int age;
    int salary;
//   这就是代码块
    {
        System.out.println("我是人");
        System.out.println("我在打工");
        System.out.println("我工资只够生活");
    }

    public B(String name) {
        this.name = name;
    }

    public B(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public B(String name, int age, int salary) {
        this.name = name;
        this.age = age;
        this.salary = salary;
    }
}
/*
我是人
我在打工
我工资只够生活
我是人
我在打工
我工资只够生活
我是人
我在打工
我工资只够生活

细节

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

  2. 类什么时候加载: 创建对象实例时 创建子类对象实例时,父类也会被加载 使用类静态成员时

  3. static{
    	System.out.println("静态代码块");
    }
    
  4. 普通代码块在使用类静态成员时不会被执行

  5. 创建一个对象时,在一个类调用顺序是:

​ 1)调用静态代码块和静态属性初始化(如果有多个静态代码块静态变量初始化,则按定义顺序调用

​ 2)调用普通代码块和普通属性初始化(如果有多个普通代码块和普通变量初始化,则按定义顺序调用

​ 3)调用构造方法

  1. 构造器的最前面其实隐含了super()和调用普通代码块

    public class A {
        public static void main(String[] args) {
            E e = new E();
        }
    }
    class D{
        D(){
            System.out.println("D");
        }
    }
    class  E extends D{
        {
            System.out.println("EEE");
        }
        E(){
            //隐藏super()
            //隐藏代码块
            System.out.println("E");
        }
    }
    /*
    D
    EEE
    E
    
  2. 创建一个子类对象时(继承关系),他们的静态代码块,静态属性初始化,普通代码块,普通属性初始化,构造方法调用顺序:

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

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

​ 3)父类的普通代码块和普通属性(优先级一样按定义顺序执行

​ 4)父类的构造方法

​ 5)子类的普通代码块和普通属性(优先级一样按定义顺序执行

​ 6)子类的构造方法

public class A {
    public static void main(String[] args) {
        CCC c = new CCC();
    }
}

class BBB{
    private  static  int n1 = getN1();
    private  int n2 = getN2();
    {
        System.out.println("BBB普通代码块");
    }
    static {
        System.out.println("BBB静态代码块");
    }

    static  int getN1(){
        System.out.println("BBB静态属性");
        return 1;
    }
    int getN2(){
        System.out.println("BBB普通属性");
        return 2;
    }

    BBB(){
        System.out.println("BBB构造方法");
    }
}

class CCC extends  BBB{
    private  static  int n01 = getN01();
    private  int n02 = getN02();
    {
        System.out.println("CCC普通代码块");
    }
    static {
        System.out.println("CCC静态代码块");
    }

    static  int getN01(){
        System.out.println("CCC静态属性");
        return 1;
    }
    int getN02(){
        System.out.println("CCC普通属性");
        return 2;
    }

    CCC(){
        //隐藏
        //super();
        //普通代码块
        System.out.println("CCC构造方法");
    }
}
/*
BBB静态属性
BBB静态代码块
CCC静态属性
CCC静态代码块
BBB普通属性
BBB普通代码块
BBB构造方法
CCC普通属性
CCC普通代码块
CCC构造方法

  1. 静态代码块只能使用静态成员,普通代码块能使用任意成员

单例设计模式

饿汉式

  1. 构造器私有化(防止用户直接new

  2. 类的内部创建对象,该对象是静态的

  3. 向外暴露一个静态的公共方法,getInstance

  4. 只要类加载,对象就被创建。

  5. 还没有用到对象,就使用(饿汉式原因

  6. 可能造成创建了对象但是没有使用

    public class A {
        public static void main(String[] args) {
    //      通过方法获得对象
            GirlFriend gif = GirlFriend.getInstance();
            System.out.println(gif.toString());
    
    //
            GirlFriend gif2 = GirlFriend.getInstance();
            System.out.println(gif2.toString());
    
            System.out.println(gif==gif2);
        }
    }
    
    //只能有一个女朋友
    class GirlFriend{
        private String name;
    //    为了在静态方法中,返回gif对象,需要将gif设置为静态
        private static GirlFriend gif = new GirlFriend("小红红");
    
        private GirlFriend(String name){
            this.name = name;
        }
    //   不需要创建对象
        public static GirlFriend getInstance(){
            return gif;
        }
    
        @Override
        public String toString() {
            return "GirlFriend{" +
                    "name='" + name + '\'' +
                    '}';
        }
    }
    /*
    GirlFriend{name='小红红'}
    GirlFriend{name='小红红'}
    true
    
    

懒汉式

  1. 构造器私有化
  2. 定义一个static对象(不初始化
  3. 提供一个public的static方法,返回gif对象
  4. 只有当用户使用getInstance才创建gif对象,后面再次调用时,直接返回gif对象
public class A {
    public static void main(String[] args) {
//      通过方法获得对象
        GirlFriend gif = GirlFriend.getInstance();
        System.out.println(gif.toString());

//
        GirlFriend gif2 = GirlFriend.getInstance();
        System.out.println(gif2.toString());

        System.out.println(gif==gif2);
    }
}

//只能有一个女朋友
class GirlFriend{
    private String name;
//    为了在静态方法中,返回gif对象,需要将gif设置为静态
    private static GirlFriend gif = null;

    private GirlFriend(String name){
        this.name = name;
    }
//   不需要创建对象
    public static GirlFriend getInstance(){
        if(gif == null){
            gif = new GirlFriend("小红红");
        }
        return gif;
    }

    @Override
    public String toString() {
        return "GirlFriend{" +
                "name='" + name + '\'' +
                '}';
    }
}
/*
GirlFriend{name='小红红'}
GirlFriend{name='小红红'}
true

对比

  1. 对象创建时机不同
  2. 懒汉式:三个线程同时进入getInstance()方法时,三个线程同时创建对象,线程不安全
  3. 饿汉式存在浪费资源问题,懒汉式不存在

内部类

局部内部类(有类名

  1. 局部内部类定义在外部类的局部位置,通常在方法或代码块
  2. 可以访问外部类的所有成员,包含私有成员
  3. 不能添加访问修饰符,但可以使用final修饰(可以在外部类的局部位置被继承
  4. 作用域:仅仅定义它的方法或代码块中,相当于局部变量
  5. 局部内部类可以直接访问外部类的成员
  6. 外部类可以创建内部类的对象,然后调用方法
  7. 如果外部类和内部类的成员重名,遵循就近原则,如果想访问外部类的成员,使用外部类名.this.成员
package 局部内部类;

public class Example {
	public static void main(String[] args) {
		Outer outer = new  Outer();
		outer.m1();
        System.out.println("Outer的hashcod"+outer);
	}
}

class Outer{
	private int n = 100;
	private void m2() {
		System.out.println("m2");
	}
	public void m1() {
		class Inner{
			public void f1() {
				System.out.println("f1");
				m2();
                System.out.println("内部类的n"+n+"外部类的n"+Outer.this.n);
                System.out.println("内部类的Outer的hashcode"+Outer.this);
			}
		}
		Inner inner = new Inner();
		inner.f1();
		
	}
	
}
/*
f1
m2
内部类的n400外部类的n100
内部类的Outer的hashcode局部内部类.Outer@24d46ca6
Outer的hashcod局部内部类.Outer@24d46ca6

匿名内部类(没有类名,!!

  1. 本质是类,该类没有名字,匿名内部类是定义在外部类的局部位置,比如方法中,而且没有类名(系统自动分配

  2. 基本语法

    new 类或接口(参数列表){
    	类体
    }
    
  3. jdk底层在创建内部类时,立马就创建了实例返回

  4. 匿名内部类使用一次就不能再使用了

  5. 可以使用外部类的所有属性

  6. 不能添加访问修饰符

  7. 作用域:方法或者代码块中

  8. 外部其他类不能访问匿名内部类

  9. 如果外部类和内部类的成员重名,遵循就近原则,如果想访问外部类的成员,使用外部类名.this.成员

    package 匿名内部类;
    
    public class AnonymousInner {
    	public static void main(String[] args) {
    		Outer outer = new Outer();
    		outer.m1();
    		
    	}
    }
    
    class Outer{
    	private int n = 100;
    	public void m1() {
    //		tiger编译类型是A接口
    //		运行类型就是匿名内部类
    //		底层分配类名 ....$...    只能用一次,但tiger可以用很多次
    		A tiger = new A(){
    			public void cry() {
    				System.out.println("Tiger叫");
    			};
    		};
    		System.out.println(tiger.getClass());
    		
    		
    //		father编译类型Father
    //		运行类型
    //		底层创建匿名内部类  class Outer$2 extends Father{}
    		Father father = new Father("老二") {
    //			匿名内部类重写test方法
    			@Override
    			public void test() {
    				// TODO Auto-generated method stub
    				super.test();
    				System.out.println("test重写");
    			}
    		};
    		System.out.println(father.getClass());
    		father.test();
    	}
    	
    	
    }
    
    interface A{
    	void cry();
    }
    
    class Father{
    	private String name;
    	public Father(String name) {
    		this.name = name;
    	}
    	
    	public void test() {
    		System.out.println("test");
    	}
    }
    /*
    class 匿名内部类.Outer$1
    class 匿名内部类.Outer$2
    test
    test重写
    
    
    package 匿名内部类;
    //直接当作参数传递	
    public class Example2 {
    	public static void main(String[] args) {
    		f1(new IL() {
    			@Override
    			public void show() {
    				// TODO Auto-generated method stub
    				System.out.println("这是一副名画");
    			}
    		});
    	}
    	
    	static public void f1(IL il) {
    		il.show();
    	}
    }
    
    
    interface IL{
    	void show();
    }
    /*
    这是一幅名画
    

成员内部类

  1. 定义在外部类的成员位置

  2. 可以使用public protected private修饰

  3. 作用域为整个类体,在外部类的成员方法中先创建内部类对象再调用方法

  4. 可以直接访问外部类的属性和方法

  5. 外部类只能创建对象再访问内部类的属性和方法

  6. 外部无关类访问内部类

    Outer outer = new Outer();
    Inner inner = Outer.new Inner();
    
    Outer.Inner inner = Outer.new Inner();
    
  7. 如果外部类和内部类的成员重名,遵循就近原则,如果想访问外部类的成员,使用外部类名.this.成员

静态内部类

  1. 放在外部类的成员位置

  2. 使用static修饰

  3. 可以直接访问外部类的所有静态成员,不能访问非静态成员

  4. 可以添加任意修饰符

  5. 作用域:整个类

  6. 外部类访问静态内部类要创建对象在调用方法

  7. 外部其他类访问静态内部类

    Outer.Inner inner =new Outer.Inner();
    
  8. 如果外部类和内部类的成员重名,遵循就近原则,如果想访问外部类的成员,使用外部类名.成员(不用加this,因为本身是静态的)

常用实用类

1. 包装类

八大包装类

黄色父类为Number,黑色部分父类为object
在这里插入图片描述

Number类
在这里插入图片描述
在这里插入图片描述

装箱拆箱

  1. jdk5之后为自动拆箱装箱
  2. int-》Integer 装箱 integer-》int 拆箱
  3. 自动装箱底层还是用的ValueIOf()方法
package 装箱拆箱;

public class Example {
	public static void main(String[] args) {
//		手动装箱
		int n = 10;
		Integer integer = new Integer(n);
		Integer integer2 = Integer.valueOf(n);
		
//		手动拆箱
		int i = integer.intValue();
		
//		jdk5后自动装箱拆箱,底层使用还是Integer.valueOf()方法
		int n2 = 200;
		Integer integer3 = n2;
		int n3 = integer3;
	}
}

  1. ==注意!!!==三元运算符要看作一个整体
Object obj1 = ture?new Integer(1):new Double(2.0);
System.out.print(obj1)
//输出结果为1.0,三元运算符看成一个整体,会提高优先级
    
Object obj2;
if(true)
    obj2 = new Integer(1);
else
    obj2 = new Double(2.0);
System.out.print(obj2);
//输出1

包装类方法

转换

public class Example2 {
	public static void main(String[] args) {
//		包装类Integer-》String
		Integer integer = 100; //自动装箱
//		1
		String str1 = integer+"";
//		2
		String str2 = integer.toString();
//		3
		String str3 = String.valueOf(integer);
		
		
//		String->转Integer
		String str4 = "1234";
//		1
		Integer integer2 = Integer.parseInt(str4);
//		2
		Integer integer3 = new Integer(str4);
		
	}
}

2. Integer

package 装箱拆箱;

public class Example3 {
	public static void main(String[] args) {
		Integer integer=new Integer(1);
		Integer jInteger = new Integer(1);
		System.out.println(integer == jInteger);//false
		
        //在-128  到  127 之间,直接从数组返回
		Integer mInteger=1;
		Integer nInteger = 1;
		System.out.println(mInteger == nInteger);//ture
		
        //超过范围,返回一个new Integer();
		Integer aInteger = 128;
		Integer bInteger = 128;
		System.out.println(aInteger == bInteger);//false
        
        //只有有基本属性,判断的是值是否相等
        Integer i11 = 127;
        int i12 = 127;
        System.out.println(i11==i12)//true
	}
}

valueof源码

public static Integer valueOf(int i) {
        if (i >= IntegerCache.low && i <= IntegerCache.high) //low-128, high127
            return IntegerCache.cache[i + (-IntegerCache.low)];
    		//IntegerCache.cache[i + (-IntegerCache.low)] 是一个已经定义好的数组 
        return new Integer(i);
    }


3. String类

  1. 字符串常量是用引号阔气的字符串序列

  2. 字符使用unicode编码,一个字符(字母或者汉字)都占两个字节

  3. 实现Serializable接口(可以串行化,在网络上传输)和Comparable接口(可以进行比较)

  4. 是一个final类,不能被继承

  5. 底层是 char value[] 数组,value 是一个final类型,不可以修改(地址不能修改)

    final char[] value = {'j','a','v','a'};
    value[0] = 'H'; //可以
    char[] v2 = {'t','o','m'};
    value = v2; //不可以修改地址
    

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-DCjurjfl-1671179039718)(E:\新建文件夹\java\image\类\String底层数组.png)]

构造字符串对象

  1. 先从常量池查看是否有“java”数据空间,如果有,直接指向,如果没有则重新创建然后指向,s最终指向的是常量池的空间地址
  2. 现在堆中创建空间,维护一个value数组,指向常量池的java空间,如果常量池没有,重新创建,如果有直接通过value指向,最终指向的是堆中的空间地址[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-DL7PdoLu-1671179039719)(E:\新建文件夹\java\image\类\创建String.png)]
  3. 当调用intern()方法时,如果池已经包含一个等于此String对象的字符串(用equals()方法确定)则返回池中的字符串,否则,将此String对象添加到池中,并返回此String对象的引用, b.intern()方法最终返回的是常量池的地址(对象)
//1.
String s= "java"

//2.
String s2 = new String("java");

//3.
char a[] = {'J','a',v','a'};
string s = new String(a);

//4 String(char a[], int startIndex, int count)
char a[] = {'1','2','3','4','5','6','7'};
String s = new String(a,2, 4) //s = '3456'

例题

例题1:

String a = "java"
String b = new String("java");
a.equals(b) //True
a == b //False a指向方法区常量池,b指向堆
a == b.intern() //True
b == b.intern() //False b指向堆,b.intern()指向常量池

例题2:

Person p1 = new Person();
Person p2 = new Person();
p1.name = 'java';
p1.name = 'java';

p1.name.equals(p2.name); //true
p1.name == p2.name; //true
p1.name == "java"; //true

String s1 = new String("abc");
String s2 = new String("abc");
s1 == s2//false        s1和s2指向堆中的value,s1和s2value地址不同,为false

例3

//1
String a = "java";
a = "python"; 
//创建了两个对象

//2
String b = "hello" + "abd";
//创建了一个对象,编译器优化:直接生成helloabd

//3
String a = "hello";
String b = "abd";
String c = a+b;
//先创建一个StringBuilder sb = new StringBuilder()
//执行sb.append()方法, 第一步sb.append("hello") 第二步sb.append("abd")
//调用sb.toString()方法  返回一个new String()
//最后其实是c指向堆中的对象(String) value[] -》 池中"helloabd"
//一共三个对象

例4

public class Example {
	String str = new String("java");
	final char[] ch = {'p','t','h'};
	
	public void change(String str, char ch[]) {
		str = "c++";
		ch[0] = 'h';
	}
	public static void main(String[] args) {
		Example example = new Example();
		example.change(example.str, example.ch);
		System.out.println(example.str+"  and  ");
		System.out.println(example.ch);//print函数的重载,print(char[] a)会直接输出char数组内容,而其他类型1数组则会输出完整类名+哈希值的16位表示
	}
}
/*
java  and  
hth

常用方法

//1. public int length()
String a = "abc";
a.length() = 3;

//2. public boolean equals(String s)
a.equals('abc') // true

//3.public boolean startsWith(String s) 
// public boolean endWith(String s) 判断字符串后缀是否以s结尾

//4.public int compareTo(String s)
//按字典序与参数s比较大小, 与s相同返回0,大于返回正值,小于返回负值
String str = 'abcd';
str.compareTo("boy") //负值

//5 public boolean contains(String s)是否包含


//6 public int indexOf(String s) 
// 字符串位置索引从0开始,判断s首次出现的位置,如果没有返回-1
//public int indexOf(String s, int startpoint) 从startpoint开始

//7 public String substring(int startpoint) 截取startpoint之后的字符串
// substring(int start, int end) 截取start到end-1

//8 public String trim() 去掉s前后空格
    
//9 equalslgnoreCase() 忽略大小写判断内容
    
//10 charAt获取某索引处的字符,注意不能使用Str[index]方式
    
//11 toUpperCase 转换成大写
    
//12 toLowerCase 转换成小写
    
//13 s1.replace(源原字符串, 替换后的字符串) 替换字符串内容,返回的结果才是替换过的,对s1没有任何影响
    
//split() 分割字符串,注意转义字符

字符串和基本数据相互转换

将由数字组成的字符从转化成数字

int x;
String s = "123";
x = Integer.parseInt(s);

使用java.lang包中的类Byte,Short,Long,Float,Double类调用相应的类方法

public static byte parseByte(String s ) throws NumberFormatException;
public static short parseShort(String s ) throws NumberFormatException;
public static long parseLong(String s ) throws NumberFormatException;
public static float parseFloat(String s ) throws NumberFormatException;
public static double parseDouble(String s ) throws NumberFormatException;

用String类中的方法将数值转换为字符串

public static String valueOf(int n);
public static String valueOf(byte n);
public static String valueOf(double n);
public static String valueOf(long n);
public static String valueOf(double n);

例子

public class transform {
    public static void main(String[] args) {
        double aver = 0, sum = 0,item = 0;
        boolean computable = true;
        String arg[] = new String[5];
        Scanner scanner = new Scanner(System.in);
        for(int i = 0; i < 5; i++) {
            arg[i] = scanner.nextLine();
        }
        for(String s:arg) {
            try{
                item = Double.parseDouble(s);
                sum = sum + item;
            }
            catch (NumberFormatException e) {
                // TODO: handle exception
                System.out.println("您输入了非数字字符串"+e);
                computable = false;
            }
        }
        if(computable) {
            System.out.println(sum);
        }
    }
}

对象的字符串表示

Object有一个public String toString() 方法,一个对象调用该方法可以获得该对象字符串表示: 创建对象的类的名字@对象的引用的字符串表示

字符串与字符,字节数组

public class Example9_6 {
    public static void main(String[] args) {
        char[] a,c;
        String string = "2009年10月1日是国庆60周年";
        a = new char[2];
        string.getChars(11, 13, a, 0);  //国庆
        /*
            getChars(int start, int end, char[] c, int offset)
            从字符串start开始end结束 复制到数组c从offset开始
        */
        System.out.println(a);
        c = "十一长假期间,学校都放假了".toCharArray();
        for(char ch: c) {
            System.out.print(ch);
        }
    }
}

public byte[] getBytes(String charsetName)得到字符串的字节数组, 汉字用两个单元

byte d = "java你好"getBytes();
// d[0] = 'j' d[4]和d[5]储存"你"  d的长度为8

正则表达式

.可以匹配任何字符
\d匹配 0~9 的所有数字
\D匹配非数字
\s匹配所有的空白字符,包括空格、制表符、回车符、换页符、换行符等
\S匹配所有的非空白字符
\w匹配所有的单词字符,包括 0~9 所有数字、26 个英文字母和下画线_
\W匹配所有的非单词字符
\\p{Punct}标点符号
X?X表达式出现零次或一次
X*X表达式出现零次或多次
X+X表达式出现一次或多次
X{n}X表达式出现 n 次
X{n,}X表达式最少出现 n 次
X{n,m}X表达式最少出现 n 次,最多出现 m 次
表示枚举例如[abc]表示 a、b、c 其中任意一个字符;[gz]表示 g、z 其中任意一个字符
表示范围:-例如[a-f]表示 a~f 范围内的任意字符;[\\u0041-\\u0056]表示十六进制字符 \u0041 到 \u0056 范围的字符。范围可以和枚举结合使用,如[a-cx-z],表示 ac、xz 范围内的任意字符
表示求否:^例如[^abc]表示非 a、b、c 的任意字符;[^a-f]表示不是 a~f 范围内的任意字符
表示“与”运算:&&例如 [a-z&&[def]]是 a~z 和 [def] 的交集,表示 d、e
f[a-z&&^bc]]是 a~z 范围内的所有字符,除 b 和 c 之外
[ad-z] [a-z&&[m-p]]是 a~z 范围内的所有字符,除 m~p 范围之外的字符
表示“并”运算并运算与前面的枚举类似。例如[a-d[m-p]]表示 [a-dm-p]

字符串替换

public String replaceAll(String regex, String replacement)

字符串分解

public String [] split(String regex)

public class Example {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        System/.out.println("一行文本");
        String string = scanner.nextLine();
        String regex = "[\\s\\p{Punct}\\d]+";
        String words[] = string.split(regex);
        for(String i:words) {
            System.out.println(i);
        }
        System.out.println(words.length);
    }
}
/*

4. StringBuffer类

结构

  1. 代表可变的字符序列,可以对字符串内容进行增删
  2. 很多方法和String一样,但是可变长度
  3. 是一个容器
  4. StringBuffer直接父类为AbstractStringBiulder,实现了Serializable(串行化)
  5. 在父类AbstractStringBiulder中,有属性char[] value,不是final,存放字符串内容,因此存放在堆中
  6. StringBuffer是一个final类

String和StringBuffer

  1. String保存的是字符串常量,里面的值不能更改,每次更新就是更改地址,效率较低(private final char value[],放在常量池
  2. StringBuufer保存到是字符粗变量,里面的值可以更改,实际上可以更新内容,不用每次更新地址(需要扩容时才更新),效率较高(char[] value 放在堆

构造器

  1. new StringBuffer(); 构造一个不带字符串的字符串缓冲区,初始容量为16

  2. new StringBuffer(100); 指定char[]的大小

  3. new StringBuffer(“hello”); char[]数组大小就是字符串长度+16

  4. String-》StringBuffer

    String str = "java";
    StringBuffer stringBuffer = new StringBuffer(str)
    //返回的是StringBuffer对象,不改变str
        
    StringBuffer stringBuffer1 = new StringBuffer();
    stringBuffer1.append(str);
    
  5. StringBuffer->String

    StringBuffer stringBuffer = new StringBuffer("java");
    String s = stringBuffer.toString();
    
    String s1 = new String(stringBuffer);
    

方法

StringBuffer append();
delete(start, end)
replace(start,end,string)
indexOf()
insert(index, str)
length

例题

String str = null;
StringBuffer stringBuffer1 = new StringBuffer();
stringBuffer1.append(str);
//输出null,stringBuffer1.length() = 4
//看源码,调用父类AbstractStringBiulder的append方法



StringBuffer stringBuffer1 = new StringBuffer(str);
//super(str.length+16)
//抛出空指针异常

5. StringBuilder

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

  2. 重载append和insert(最主要的方法

  3. 父类也是AbstractStringBuilder

  4. 其方法没有sychornized

String,StirngBuffer,StringBiulder

String:不可变字符序列,效率低,复用率高

StringBuffer : 可变序列,效率较高(增删)线程安全

StringBuilder:可变序列,效率最高,线程不安全

如果对Stirng做大量修改,不能使用String(会产生大量副本)

如果存在大量修改,一般使用StringBuffer或StringBuilder

如果存在大量修改,多线程,用StringBuffer

如果存在大量修改,单线程,用StringBuilder

很少修改,被多个对象引用,用String,比如配置信息

package String;

public class compare {
	public static void main(String[] args) {
		String textString = "";
		StringBuffer stringBuffer = new StringBuffer();
		StringBuilder stringBuilder = new StringBuilder();
		
		long starttime = System.currentTimeMillis();
		for(int i = 0; i<100000;i++) {
			stringBuffer.append(String.valueOf(i));
		}
		long endtime = System.currentTimeMillis();
		System.out.println("StringBuffer运行时间"+(endtime-starttime));
		
		long starttime1 = System.currentTimeMillis();
		for(int i = 0; i<100000;i++) {
			stringBuilder.append(String.valueOf(i));
		}
		long endtime1 = System.currentTimeMillis();
		System.out.println("StringBuilder运行时间"+(endtime1-starttime1));
		
		
		long starttime2 = System.currentTimeMillis();
		for(int i = 0; i<100000;i++) {
			textString = textString + i;
		}
		long endtime2 = System.currentTimeMillis();
		System.out.println("String运行时间"+(endtime2-starttime2));
		
	}
}
/*
StringBuffer运行时间14
StringBuilder运行时间4
String运行时间4567


6. StringTokenizer类

常用构造方法

StringTokenizer(String s)

使用默认的分隔符标记,即空格、换行符、回车符、tab符

StringTokenizer(String s, String delim)

使用delim作为分割标记

常用方法

countTokens() 得到分析其中计数变量的值

nextToken() 在字符串中获得下一个语言符号, 计数变量的值就会自动减一

hasMoreTokens() 通常用while循环来逐个获取语言符号,为了控制循环使用该方法

import java.util.StringTokenizer;

public class Example {
    public static void main(String[] args) {
        String s = "you are welcome(thank you), nice to meet you";
        StringTokenizer fenxi = new StringTokenizer(s,"() ,");
        System.out.println(fenxi.countTokens());   //9
        while(fenxi.hasMoreTokens()) {
            System.out.print(fenxi.nextToken()+" ");
        }
        //you are welcome thank you nice to meet you 
        System.out.println("\n"+fenxi.countTokens()); //0
    }
}

7.Arrays类

Arrays.toString(arr); //显示数组 

sort(arr)
//sort()可以重载,sort(arr, comparator<>) 指定排序规则
    
binarySearch (arr, index)
//通过二分法查找
    
copyOf(arr, arr.length)
//从arr数组中,拷贝arr.length个元素到newArr中
//如果拷贝长度>arr.length就在新数组后面加null
//如果拷贝长度<0就抛出异常
//底层使用的是System.array.copy()
    
fill(arr, value)
//数组填充,使用value填充arr
    
equals()
//比较两个数组元素内容是否完全一致
    
    
asList
//将一组值转换成list集合

Scanner类

用来解析字符串或者文件;

hasNextline判断是否有下一行

hasNext判断是否有下一个单词

Date类

构造Date对象

1.使用无参构造方法获取本地实际

Date date = new Date()

2.使用带参数的构造方法

Date date = new Date(1000)

计算机系统公元时间是1970年1月1日0时, 北京时间8时

默认表示:星期、月、日、小时、分、秒、年

日期格式化

使用DateFormat子类SimpleDateFormat来实现日期格式化

SimpleDateFormat SDF = new SimpleDateFormat(pattern)

pattern是由普通字符和一些称作格式符组成的字符序列

( d )将日显示为不带前导零的数字(如 1) 。
( dd )将日显示为带前导零的数字(如 01)。
( ddd )将日显示为英文缩写形式(如 Sun)。
( dddd )将日显示为英文全名(如 Sunday)。
MM将月份显示为带前导零的数字(如 01/12/05)。
MMM将月份显示为英文缩写形式(如 Jan)。
MMMM将月份显示为完整月份名(如 January)。
hh使用12小时制将小时显示为带前导零的数字(如 01:15:15 PM)。
HH使用24小时制将小时显示为带前导零的数字(如 01:15:15)。
mm将分钟显示为带前导零的数字(如 12:01:15)。
ss将秒显示为带前导零的数字(如 12:15:05)。
yyyy以四位数字格式显示年份。

Calendar类

Math类

两个静态常量 E PI

long abs(double a) 返回a的绝对值

double max(double a, double b) 返回ab最大值

double min(double a, double b) 返回ab最小值

double pow(double a,double b) 返回a的b次幂

double sqrt(double a) 返回a的平方根

double cbrt(double a )返回a的立方根

double log(double a) 返回a的对数 以E为底

double sin(double a) 返回正弦值

double asin(double a) 返回反正弦值

BigDecimal类

因为不论是float 还是double都是浮点数,而计算机是二进制的,浮点数会失去一定的精确度

注:根本原因是:十进制值通常没有完全相同的二进制表示形式;十进制数的二进制表示形式可能不精确。只能无限接近于那个值

1、参数类型为double的构造方法的结果有一定的不可预知性。有人可能认为在Java中写入newBigDecimal(0.1)所创建的BigDecimal正好等于 0.1(非标度值 1,其标度为 1),但是它实际上等于0.1000000000000000055511151231257827021181583404541015625。这是因为0.1无法准确地表示为 double(或者说对于该情况,不能表示为任何有限长度的二进制小数)。这样,传入到构造方法的值不会正好等于 0.1(虽然表面上等于该值)。

​ 2、另一方面,String 构造方法是完全可预知的:写入 newBigDecimal(“0.1”) 将创建一个 BigDecimal,它正好等于预期的 0.1。因此,比较而言,通常建议优先使用String构造方法

[https://blog.csdn.net/qq_35868412/article/details/89029288]:

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.text.DecimalFormat;

public class Example {
	public static void main(String[] args) {
		System.out.println(2.0-1.1);
//		构造方法 常用BigDecimal(String)
		BigDecimal a = new BigDecimal("2.0000");
		BigDecimal b = new BigDecimal("1.1000");
		System.out.println(a.subtract(b));
		System.out.println(a.divide(b, 2, RoundingMode.HALF_UP));
		BigDecimal c = new BigDecimal("3.1415926");
		System.out.println(c.setScale(2,RoundingMode.HALF_UP));
	}
}
/*舍入模式
ROUND_CEILING    //向正无穷方向舍入
ROUND_DOWN    //向零方向舍入
ROUND_FLOOR    //向负无穷方向舍入
ROUND_HALF_DOWN    //向(距离)最近的一边舍入,除非两边(的距离)是相等,如果是这样,向下舍入, 例如1.55 保留一位小数结果为1.5
ROUND_HALF_EVEN    //向(距离)最近的一边舍入,除非两边(的距离)是相等,如果是这样,如果保留位数是奇数,使用ROUND_HALF_UP,如果是偶数,使用ROUND_HALF_DOWN
ROUND_HALF_UP    //向(距离)最近的一边舍入,除非两边(的距离)是相等,如果是这样,向上舍入, 1.55保留一位小数结果为1.6,也就是我们常说的“四舍五入”
ROUND_UNNECESSARY    //计算结果是精确的,不需要舍入模式
ROUND_UP    //向远离0的方向舍入

Pattern和Match类

字符串匹配

System类

exit 退出当前程序 Sustem.exit()

gc 运行垃圾回收机制, System.gc()

currentTimeMillens返回当前距离1970-1-1的毫秒数

集合

1. 集合的理解和好处

  1. 可以动态保存任意多个对象,使用比较方便
  2. 提供一系列方便操作对象的方法,add、remove、set、get
  3. 使用集合添加删除新元素的示意代码 --简洁了

2.集合的框架体系

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-opFgAa6N-1671179039720)(E:\新建文件夹\java\image\集合\集合框架.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fNBRUlak-1671179039720)(E:\新建文件夹\java\image\集合\集合框架2.png)]

//1集合主要是两组(单列集合,双列集合) 区别如下代码, 双列需键和值

//2. Collection 接口有两个重要的子接口 List Set , 他们的实现子类都是单列集合

//3. Map 接口的实现子类 是双列集合,存放的 K-V

import java.util.ArrayList;
import java.util.HashMap;

public class Collction_ {
	public static void main(String[] args) {
		ArrayList arrayList = new ArrayList();
		arrayList.add("小明");
		arrayList.add("小兰");
		
		HashMap hashMap = new HashMap();
		hashMap.put("NO1", "北京");
		hashMap.put("NO2", "上海");
	}
}

3.Collection接口和常用方法

collection介绍

  1. collection实现子类可以存放多个元素,每个元素可是Object

  2. collection的实现类可以存放重复的元素,有些不可以

  3. collection的实现类,有些是有序的(list) 有些是无序的(set)

  4. 没有直接的实现子类,是通过它的子接口Set和List来实现的

常用方法

以实现子类ArrayList演示

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

public class Collction_ {
	@SuppressWarnings({ "all"})
	public static void main(String[] args) {
		List arrayList = new ArrayList();
		
//		添加元素add()
		arrayList.add("小梦");
		arrayList.add(22);  //实际上是添加的Integer对象
		arrayList.add(true);
		System.out.println(arrayList);
		
//		删除元素remove() 可以加位置,可以加具体值
		arrayList.remove(0);
//		arrayList.remove(true);
		System.out.println(arrayList);
		
//		是否含有contains()
		System.out.println(arrayList.contains(22));
		
//		获取元素个数size()
		System.out.println(arrayList.size());
		
//		判断是否为空 isEmpty()
		System.out.println(arrayList.isEmpty());
		
//		清空clear()
		arrayList.clear();
		System.out.println(arrayList);
		
//		添加多个元素addAll()
		List list2 = new ArrayList();
		list2.add("三国演义");
		list2.add("红楼梦");
		arrayList.addAll(list2);
		System.out.println(arrayList);
		
//		判断多个元素是否存在containsAll()
		System.out.println(arrayList.containsAll(list2));
		
//		删除多个元素removeAll()
		arrayList.removeAll(list2);
		System.out.println(arrayList);
		
	}
}
/*运行结果
[小梦, 22, true]
[22, true]
true
2
false
[]
[三国演义, 红楼梦]
true
[]

Collection接口遍历元素方式

  1. Iterator对象称为迭代器,主要用于遍历Collection集合中的元素
  2. 所有实现了Collection接口的集合类都有一个iterator()方法
  3. Iterator仅用于遍历集合,Iterator本身不存放对象
  4. 结构如图(在调用next()方法之前必须调用hasNext()

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jC8n1aU4-1671179039720)(E:\新建文件夹\java\image\集合\迭代器执行原理.png)]

import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;

public class Iterator_ {
	@SuppressWarnings({"all"})
	public static void main(String[] args) {
		Collection arrayList = new ArrayList();
		arrayList.add(new Book("三国演义", "罗贯中", 12) );
		arrayList.add(new Book("红楼梦", "曹雪芹", 19) );
		arrayList.add(new Book("水浒传", "施耐庵", 30) );
//		System.out.println(arrayList);
        
//      增强for循环
//      1.
//		for (Iterator iterator = arrayList.iterator(); iterator.hasNext();) {
//			Object object = iterator.next();
//			System.out.println(object);
//			
//		}
        
//      2.        
//       for(Object book: arrayList) {
//			System.out.println(book);
//		}
		
		Iterator iterator = arrayList.iterator();
		while (iterator.hasNext()) {
			Object object = (Object) iterator.next();
			System.out.println(object);
		}
		
//		第二次使用迭代器需重置
		iterator = arrayList.iterator();
		while (iterator.hasNext()) {
			Object object = (Object) iterator.next();
			System.out.println(object);
		}
		
	}
}

class Book{
	private String name;
	private String author;
	private int price;
	
	public Book(String name,String author,int price) {
		this.name = name;
		this.price = price;
		this.author = author;
		// TODO Auto-generated constructor stub
	}
	
	@Override
	public String toString() {
		// TODO Auto-generated method stub
		return "书名:" + name + "  作者:" + author + "  价格:" +price;
	}
}
/*运行结果
书名:三国演义  作者:罗贯中  价格:12
书名:红楼梦  作者:曹雪芹  价格:19
书名:水浒传  作者:施耐庵  价格:30
书名:三国演义  作者:罗贯中  价格:12
书名:红楼梦  作者:曹雪芹  价格:19
书名:水浒传  作者:施耐庵  价格:30

4.List接口(继承collection

(1)List接口介绍

List接口是collection接口的子接口
  1. 元素有序,即添加按顺序和取出顺序一致,且可重复
  2. 每个元素都有其对应的顺序索引,从0开始
  3. list实现类(vector,linklist)遍历方法一样
List常用接口和方法
import java.util.ArrayList;
import java.util.List;

public class List_ {
	@SuppressWarnings({"all"})
	public static void main(String[] args) {
		List list = new ArrayList();
		list.add("喜羊羊");
		list.add("懒羊羊");
		list.add("沸羊羊");
		list.add("暖羊羊");

//		void add(int index, Object ele):在 index 位置插入 ele 元素
		list.add(1, "美羊羊");
		System.out.println(list);
		
//		boolean addAll(int index, Collection eles):
//			从 index 位置开始将 eles 中的所有元素添加进来
		List list2 = new ArrayList(); 
		list2.add("jack"); 
		list2.add("tom"); 
		list.addAll(1, list2);
		System.out.println(list);
		
//		Object get(int index):获取指定 index 位置的元素
		System.out.println(list.get(3));
		
//		int indexOf(Object obj):返回 obj 在集合中首次出现的位置
		System.out.println(list.indexOf("jack"));
		
//		int lastIndexOf(Object obj):返回 obj 在当前集合中末次出现的位置
		list.add(4,"jack");
		System.out.println(list.lastIndexOf("jack"));
		
//		Object remove(int index):移除指定 index 位置的元素,并返回此元素
		
		System.out.println(list.remove(1));
		System.out.println(list);
		
//		Object set(int index, Object ele):
//		    设置指定 index 位置的元素为 ele , 相当于是替换
		list.set(0, "喜羊羊1");
		System.out.println(list);
		
//		List subList(int fromIndex, int toIndex):
//			返回从 fromIndex 到 toIndex 位置的子集合[fromIndex, toIndex)
//		注意返回的子集合 fromIndex <= subList < toIndex
		System.out.println(list.subList(2, 5));

		
	}
}
/*
[喜羊羊, 美羊羊, 懒羊羊, 沸羊羊, 暖羊羊]
[喜羊羊, jack, tom, 美羊羊, 懒羊羊, 沸羊羊, 暖羊羊]
美羊羊
1
4
jack
[喜羊羊, tom, 美羊羊, jack, 懒羊羊, 沸羊羊, 暖羊羊]
[喜羊羊1, tom, 美羊羊, jack, 懒羊羊, 沸羊羊, 暖羊羊]
[美羊羊, jack, 懒羊羊]
简单排序
package List接口;

import java.util.*;

@SuppressWarnings({"all"})
public class ListSort {
	
	public static void main(String[] args) {
		List list = new ArrayList();
		list.add(new Book("三国演义", "罗贯中", 12) );
		list.add(new Book("红楼梦", "曹雪芹", 69) );
		list.add(new Book("水浒传", "施耐庵", 30) );
		
		System.out.println("===排序前===");
		for(Object obj:list) {
			System.out.println(obj);
		}
		
		
		Book book = new Book();
		Book.bubuSort(list);
		System.out.println("===排序后===");
		for(Object obj:list) {
			System.out.println(obj);
		}
	}
}



class Book {
	private String name;
	int price;
	private String author;
	
	public Book() {
		// TODO Auto-generated constructor stub
	}
	public Book(String name, String author, int price) {
		// TODO Auto-generated constructor stub
		this.name = name;
		this.price = price;
		this.author = author;
	}
	
	@Override
	public String toString() {
		// TODO Auto-generated method stub
		return "name:"+name+"\tprice:"+price+"\tauthor:"+author;
	}
	
	public static void bubuSort(List list) {
		int listsize = list.size();
		for(int i = 0; i < listsize-1 ; i++) {
			for(int j = 0; j < listsize-1-i; j++) {
				Book book1 = (Book)list.get(j);
				Book book2 = (Book)list.get(j+1);
				if(book1.price > book2.price) {
					list.set(j, book2);
					list.set(j+1, book1);
				}
			}
		}
		
	}
}
/*
===排序前===
name:三国演义	price:12	author:罗贯中
name:红楼梦	price:69	author:曹雪芹
name:水浒传	price:30	author:施耐庵
===排序后===
name:三国演义	price:12	author:罗贯中
name:水浒传	price:30	author:施耐庵
name:红楼梦	price:69	author:曹雪芹

(2)ArrayList

ArrayList底层结构和源码分析

注意事项

  1. 可以存放所有元素包括空元素
  2. 是由数组来实现数据存储的
  3. 基本等同于Vector,除了ArrayList是线程不安全(但执行效率高)多线程 不建议使用ArayyList

底层操作机制

  1. ArrayList中维护了一个Object类型的数组elementData(transient Object[] elementData transient表示瞬间,表示该属性不会被序列化
  2. 当创建对象时,如果使用无参构造器,则初始elementData容量为0,第一次添加,扩容为10,如需再次扩容,则扩容为1.5倍
  3. 如果用指定大小构造器,初始化容量为指定大小,如需扩容,按当前1.5倍
    在这里插入图片描述

当执行add()方法时,首先会调用ensureCapacityInternal()这个方法,来保证容量足够,之后再将新添加的元素e放到新扩充容量的位置。我们再来细看ensureCapacityInternal()这个方法

在这里插入图片描述

在这里插入图片描述

ensureCapacityInternal()上面传入的参数是size+1,size是当前数组的元素个数大小。方法进来以后,先判断当前数组是否为空数组,DEFAULTCAPACITY_EMPTY_ELEMENTDATA是ArrayList前面定义的空数组,如果是的话,则取定义的最小容量和默认大小的最大值,DEFAULT_CAPACITY大小前面定义为10。之后在进行ensureExplicitCapacity(minCapacity)的方法。

如果新扩充容量大于目前元素的大小时,则终于开始进行扩容grow()方法。

在grow()方法中,首先对旧容量oldCapacity进行1.5倍的扩容,之后如果newCapacity仍然小于最小要求容量minCapacity,就把minCapacity的值给到newCapacity。如果newCapacity的值大于最大要求容量,则会走hugeCapacity()方法,进行一个最终扩容后的大小确认,最后调用Arrays.copyof()方法,在原数组的基础上进行复制,容量变为newCapacity的大小。

在这里插入图片描述

扩容使用的是Arrays.copyOf(),扩容后的地址会改变,输入长度为实际有效数值的长度,不包含null。容量和size是有区别的

(3)Vector底层结构

介绍
  1. vector底层也是一个对象数组 ,protected Object[] elementData
  2. vector 是线程同步的,即线程安全,Vector每个方法都带有synchronized
  3. 开发中需要线程同步安全时考虑使用vector
  4. 扩容机制: 无参默认十,满后两倍扩容 如果指定大小每次按两倍扩容

(4)LinkedList底层结构

linkedlist介绍
  1. linkedlist底层维护了一个双向链表(!!!
  2. 维护了两个属性first和last分别指向首节点和尾节点
  3. 每个节点(Node对象),里面又维护了prev,next,item三个属性,其中通过prev指向前一个,next指向后一个节点,实现双向链表
  4. LinkedList的删除和添加不是以来数组完成的,所以效率高
底层源码:
  1. 增加

在这里插入图片描述在这里插入图片描述

第一次添加节点如图

在这里插入图片描述

第二次添加节点如图
在这里插入图片描述

  1. 删除

    (1) remove() 默认删除头节点

    public E remove() {
            return removeFirst();
        }
    
    
    
    public E removeFirst() {
            final Node<E> f = first;
            if (f == null)
                throw new NoSuchElementException();
            return unlinkFirst(f);
        }
    
    
    private E unlinkFirst(Node<E> f) {
            // assert f == first && f != null;
            final E element = f.item;
            final Node<E> next = f.next;
            f.item = null;
            f.next = null; // help GC
            first = next;
            if (next == null)
                last = null;
            else
                next.prev = null;
            size--;
            modCount++;
            return element;
        }
    

    (2) remove(Object o ) 删除指定元素

    (3) remove(int index ) 删除指定位置的元素

  2. set(int index ,Object o ) 改变指定位置的元素

  3. get(int index) 获得指定位置的元素

5.Set接口(继承collection

(1)Set接口介绍

  1. set接口实现类的对象(set接口对象)不能存放重复的元素,可以放null
  2. set接口对象存放数据是无序的,添加和取出的顺序不一致
  3. 取出的顺序虽不是添加的顺序,但是他是固定的
  4. 不可以通过索引取出元素
  5. 继承了collection接口,遍历方法与collection遍历相同
  6. 常用方法 add() /返回boolean值,是否添加成功 remove(Object o)

(2)HashSet

hashset介绍
  1. HashSet实现了set接口

  2. HashSet实际上是HashMap

  3. 可以存放null值但只能有一个

  4. hashset不保证元素是有序的,取决于hash后,在确定索引的结果

    public HashSet(){
        map = new HashMap<>();
    }                                      
    
  5. 不能有重复元素/对象

package HashMap_;

import java.util.HashSet;
import java.util.Set;


public class HashMap_ {
	public static void main(String[] args) {
		Set set = new HashSet();
		System.out.println(set.add("小红"));//true
		System.out.println(set.add("John"));//true
		System.out.println(set.add("Jack"));//true
		System.out.println(set.add("小红"));//false重复元素
		System.out.println(set);
		//[小红, John, Jack]
		
		set = new HashSet();
		System.out.println(set.add(new Dog("big yellow")));//true
		System.out.println(set.add(new Dog("big yellow")));//true
		System.out.println(set);
		//[name:big yellow, name:big yellow]分析源码阶段解释原因
        
		System.out.println(set.add(new String("hong")));//true
		System.out.println(set.add(new String("hong")));//false
		System.out.println(set);
		//[hong, name:big yellow, name:big yellow] 为什么都是new一个对象结果却不同?
	}
}

class Dog{
	String name;
	public Dog(String name) {
		// TODO Auto-generated constructor stub
		this.name = name;
	}
	
	@Override
	public String toString() {
		// TODO Auto-generated method stub
		return "name:"+ name;
	}
}

hashset添加元素底层
  1. 底层是HashMap,HashMap底层是(数组+链表+红黑树)

  2. 添加元素时,先得到hash值,转成-》索引值 hash()+equals()

  3. 找到存储数据表table,看这个索引位置是否已经存放了元素

  4. 如果没有,直接加入

  5. 如果没有,调用equals比较,如果相同,就放弃添加,如果不同,则添加到最后(字符串的equals方法被改写,比较内容) equals()方法由程序员决定,不能够简单的理解字符串的内容

  6. 数组(table)加链表示意图[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vGZCUpeT-1671179039722)(E:\新建文件夹\java\image\集合\hashset\数组加链表示意图.png)]

  7. 在Java8中,如果一条链表元素个数超过TREEIFY_THRESHOLD(默认8),并且table大小》=MIN_TREEIFY_CAPACITY(默认64),就会进行树化(红黑树

  8. 底层源码:

    public boolean add(E e) {
            return map.put(e, PRESENT)==null;
        }
    
    
     public V put(K key, V value) {
            return putVal(hash(key), key, value, false, true);
        }
    
    //hash值并不是hashcode,做过处理,为了避免碰撞
    static final int hash(Object key) {
            int h;
            return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
        }
    
    //核心代码 HashMap类
    final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
                       boolean evict) {
            Node<K,V>[] tab; Node<K,V> p; int n, i;//定义辅助变量
            //table是hashmap的一个属性,类型为Node[]
            if ((tab = table) == null || (n = tab.length) == 0)
                n = (tab = resize()).length; //跳入resize()函数,此后table变为16个大小,第一次扩容
          
            //(1) 根据key,得到hash,计算key应该存放在table的哪个位置,然后赋值给p
            //(2) 判断p是否为空
            //(2.1) 如果p为空,则表示没有元素,创建应该Node(key, value)  最重要的两个值
            if ((p = tab[i = (n - 1) & hash]) == null)  //无论是否符合都会执行p = tab[i = (n - 1)] 让p指向计算出的索引的第一个元素
                tab[i] = newNode(hash, key, value, null);  //传hash为了判断该位置是否有元素
            else {
                Node<K,V> e; K k;
                //如果当前索引对应链表的第一个元素与准备添加的key的hash相同,
                //并且(p指向的node节点的key与准备加入的key是同一个对象)或者(p指向的node节点的key的equals()和准备加入到key比较后相同)    
                //则不能添加
                if (p.hash == hash &&   
                    ((k = p.key) == key || (key != null && key.equals(k))))
                    e = p;  
                //判断p是否为一棵红黑树,如果是,调用putTreeVal进行添加
                else if (p instanceof TreeNode)
                    e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
                else {
                    //如果table索引位置已经是一个链表则使用for循环进行比较
                    //(1)依次比较链表中每一个元素,不相同,则加入到链表最后一个(第一个if语句 
                    //把元素添加到链表后,立即判断链表是否已经达到8个节点,如果是调用treeifyBin(tab, hash);对该链表进行树化(转成红黑树
                    //转成红黑树时要先进行判断,判断条件:
                    //if (tab == null || (n = tab.length) < MIN_TREEIFY_CAPACITY)
                    // 如果条件成立,则先对table进行扩容,不成立在进行树化
                    //(2)比较过程中,遇到相同的直接退出(第二个if语句
                    for (int binCount = 0; ; ++binCount) {
                        if ((e = p.next) == null) {//把p的下一个元素赋给e
                            p.next = newNode(hash, key, value, null);
                            if (binCount >= TREEIFY_THRESHOLD(8) - 1) // -1 for 1st
                                treeifyBin(tab, hash);
                            break;
                        }
                        if (e.hash == hash &&
                            ((k = e.key) == key || (key != null && key.equals(k))))
                            break;
                        p = e;  //p往下移
                    }
                }
                if (e != null) { // existing mapping for key
                    V oldValue = e.value;
                    if (!onlyIfAbsent || oldValue == null)
                        e.value = value;
                    afterNodeAccess(e);
                    return oldValue;
                }
            }
        
        
        
            ++modCount;
            if (++size > threshold)  //判断放入元素之后大小是否超过临界值
                resize();
            afterNodeInsertion(evict); //空方法   为了让hashmap子类实现
            return null; //代表成功  (传回put()方法,再传回add()方法,在此方法中判断是否为null,返回bool值
        }
    
    
    
    
    final Node<K,V>[] resize() {
            Node<K,V>[] oldTab = table;
            int oldCap = (oldTab == null) ? 0 : oldTab.length;
            int oldThr = threshold;
            int newCap, newThr = 0;
            if (oldCap > 0) {
                if (oldCap >= MAXIMUM_CAPACITY) {
                    threshold = Integer.MAX_VALUE;
                    return oldTab;
                }
                else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY &&
                         oldCap >= DEFAULT_INITIAL_CAPACITY)
                    newThr = oldThr << 1; // double threshold
            }
            else if (oldThr > 0) // initial capacity was placed in threshold
                newCap = oldThr;
            else {               // zero initial threshold signifies using defaults
                newCap = DEFAULT_INITIAL_CAPACITY;
                newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY);
                //newThr为临界值,防止数据大量添加    DEFAULT_LOAD_FACTOR=0.75  DEFAULT_INITIAL_CAPACITY = 16
            }
            if (newThr == 0) {
                float ft = (float)newCap * loadFactor;
                newThr = (newCap < MAXIMUM_CAPACITY && ft < (float)MAXIMUM_CAPACITY ?
                          (int)ft : Integer.MAX_VALUE);
            }
            threshold = newThr;
            @SuppressWarnings({"rawtypes","unchecked"})
            Node<K,V>[] newTab = (Node<K,V>[])new Node[newCap];
            table = newTab;
            if (oldTab != null) {
                for (int j = 0; j < oldCap; ++j) {
                    Node<K,V> e;
                    if ((e = oldTab[j]) != null) {
                        oldTab[j] = null;
                        if (e.next == null)
                            newTab[e.hash & (newCap - 1)] = e;
                        else if (e instanceof TreeNode)
                            ((TreeNode<K,V>)e).split(this, newTab, j, oldCap);
                        else { // preserve order
                            Node<K,V> loHead = null, loTail = null;
                            Node<K,V> hiHead = null, hiTail = null;
                            Node<K,V> next;
                            do {
                                next = e.next;
                                if ((e.hash & oldCap) == 0) {
                                    if (loTail == null)
                                        loHead = e;
                                    else
                                        loTail.next = e;
                                    loTail = e;
                                }
                                else {
                                    if (hiTail == null)
                                        hiHead = e;
                                    else
                                        hiTail.next = e;
                                    hiTail = e;
                                }
                            } while ((e = next) != null);
                            if (loTail != null) {
                                loTail.next = null;
                                newTab[j] = loHead;
                            }
                            if (hiTail != null) {
                                hiTail.next = null;
                                newTab[j + oldCap] = hiHead;
                            }
                        }
                    }
                }
            }
            return newTab;
        }
    
扩容机制
  1. HashSet底层是HashMap,第一次添加时,table数组扩容到16,临界值(threshold)是16*加载因子(loadFactor)是0.75 = 12
  2. 如果table数组使用到了临界值12,就会扩容到16 * 2=32,新的临界值变为32 * 0.75=24,以此类推
  3. 在java8中,一条链表的元素个数到达TREEIFY_THRESHOLD(默认8)并且table大小>=MIN_TREEIFY_CAPACITY(默认64),就会进行树化,否则仍然采用数组扩容
  4. size()指的是 加入的元素总数(包括链表上的数),而不单单是table上的元素
例题

得出hashcode-》推出hash值-》euqals()判断-》放入

public class Hello {
    public static void main(String[] args) {
        HashSet hashSet = new HashSet();
        hashSet.add(new Employee("MILAN",18));
        hashSet.add(new Employee("MILAN",18));
        hashSet.add(new Employee("MILAN",18));
        System.out.println(hashSet);


    }
}

class Employee{
    private  String  name;
    private  int age;

    public Employee(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 String toString() {
        return "Employee{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Employee employee = (Employee) o;
        return age == employee.age && Objects.equals(name, employee.name);
    }

    @Override
    public int hashCode() {
        return Objects.hash(name, age);
    }
    //只要name和age一样那么返回的hashCode就一样
    //原理,用name和age的hashCode返回对象的hashCode
}

hash的实现:根据每个元素的hashCode 通过算法返回一个hashCode

public static int hash(Object... values) {
        return Arrays.hashCode(values);
    }

public static int hashCode(Object[] a) {
        if (a == null)
            return 0;

        int result = 1;

        for (Object element : a)
            result = 31 * result + (element == null ? 0 : element.hashCode());

        return result;
    }

(3)LinkedHashSet

LinkedHashSet介绍
  1. LinkedHashSet是HashSet的子类
  2. 底层是应该LinkedHashMap,底层维护了一个数组加双向链表(有head和tail
  3. 根据元素的hashCode值来决定元素的存储位置,用链表维护元素的次序。[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9mldgDIu-1671179039723)(E:\新建文件夹\java\image\集合\linkedhashset\数组+双向链表.png)]
  4. 每一个节点都有pre 和next属性,形成双向链表
  5. 添加一个元素时,先求hash值,再求索引,确定该元素在table的位置,然后讲添加的元素加入到双线链表(如果已经存在不添加,原则和hashset相同) tail.next = new Element newElement.pre = tail tail = newElement
  6. 这样的化我们遍历LinkedHashSet也能确保插入顺序和遍历顺序一致
底层源码
  1. LinkedHashSet加入元素和取出元素的顺序一致
  2. LinkedHashSet底层维护一个LinkedHashMap(是HashMap的子类
  3. LinkedHashSet底层结构(数组table+双线链表
  4. 添加第一次时,直接数组table扩容到16,存放的结点是LinkedHashMap$Entry类型
  5. 数组是HashMap$Node[]类型
  6. add()方法仍然是HashMap中的putVal()方法
//linkedhashmap类
//Entry继承HashMap.Node(Node是内部静态类,可以通过类名直接访问),故table是Node类,而元素是Entry类
static class Entry<K,V> extends HashMap.Node<K,V> {
        Entry<K,V> before, after;
        Entry(int hash, K key, V value, Node<K,V> next) {
            super(hash, key, value, next);
        }
    }

(4)TreeSet类

  1. 使用无参构造器,元素仍然是无序的

  2. 使用TreeSet提供的一个构造器,可以传入一个比较器(匿名内部类)并指定排序规则

  3. 构造器把传入的比较器对象,赋给了 TreeSet 的底层的 TreeMap 的属性 this.comparator ,

    public TreeMap(Comparator<? super K> comparator) {

    ​ this.comparator = comparator;

    }

    //hashmap类
    private V put(K key, V value, boolean replaceOld) {
            Entry<K,V> t = root;//赋给t根节点
            if (t == null) {
                addEntryToEmptyMap(key, value);
            	/*private void addEntryToEmptyMap(K key, V value) {
            		compare(key, key); // type (and possibly null) check判断是否为空
           			root = new Entry<>(key, value, null);
            		size = 1;
           			modCount++;
        		}
        		*/
                return null;
            }
            int cmp;
            Entry<K,V> parent;
            // split comparator and comparable paths
            Comparator<? super K> cpr = comparator;  //将匿名类赋值给cpr
            if (cpr != null) {  //有参构造器,cpr一定不为空,if后有else此时省略
                do {
                    parent = t;//不断更新父节点
                    cmp = cpr.compare(key, t.key); //动态绑定到匿名内部类cpr
                    if (cmp < 0)
                        t = t.left;
                    else if (cmp > 0)
                        t = t.right;
                    else {   //如果相等,则返回0,这个key就没有加入
                        V oldValue = t.value;
                        if (replaceOld || oldValue == null) {
                            t.value = value;
                        }
                        return oldValue;
                    }
                } while (t != null);
            addEntry(key, value, parent);
            return null;
        }
    
    //例
    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)o2).compareTo((String) o1);
                }
            });
            treeSet.add("jack");
            treeSet.add("tom");
            treeSet.add("sp");
            treeSet.add("a");
            System.out.println(treeSet);
        }
    }
    

6.Map接口和常用方法

(1)Map接口介绍

  1. Map和Collection并列存在,用于保存具有映射关系的数据:Key–Value

  2. Map中的key和value是任何引用类型的数据,会封装到HashMap$Node对象中

  3. Map中的key不允许重复,当有相同的Key时,等价于替换,原因和HashSet一样,前面分析过源码

  4. Map中的value可以重复

  5. Map中key和value都可以为空,但key只能有一个空,value能有多个空

  6. package Map接口;
    
    import java.util.HashMap;
    import java.util.Map;
    
    
    @SuppressWarnings({"all"})
    public class Map_ {
    	public static void main(String[] args) {
    		Map map = new HashMap();
    		map.put("no1", "xyy");
    		map.put("no2", "myy");
    		map.put("no1", "nyy");//等价替换
    		System.out.println("map:"+map);
    		
    		map.put("no3", "xyy");
    		System.out.println("map:"+map);
    		
    		map.put(null, null);
    		System.out.println("map:"+map);
    		
    		map.put(null, "fyy");
    		System.out.println("map:"+map);
    		
    		map.put("no4", null);
    		map.put("no5", null);
    		System.out.println("map:"+map);
    	}
    }
    
    /*
    map:{no2=myy, no1=nyy}
    map:{no2=myy, no1=nyy, no3=xyy}
    map:{no2=myy, null=null, no1=nyy, no3=xyy}
    map:{no2=myy, null=fyy, no1=nyy, no3=xyy}
    map:{no2=myy, null=fyy, no1=nyy, no4=null, no3=xyy, no5=null}
    
    
  7. 一般String类作为Map的key,实际上不仅String可以(只要是Object子类都可以

  8. key和value唯一对应

底层源码
  1. Map存放数据key–value示意图:

在这里插入图片描述
在这里插入图片描述

  1. k-v 最后是HashMap$Node node = newNode(hash,key,value,null)

  2. k-v 为了方便程序员遍历, 还会创建EntrySet集合,该集合存放的元素类型是Entry,而一个Entry对象就有 k-v EntrySet<Entry<K,V>>, 即 :tansient Set<Map.Entry<K,V>> entrySet

  3. EntrySet中,定义的类型是Map.Entry 但是实际上存放的还是HahsMap$Node (HashMap Node实现了Map.Entry接口,

  4. Map.Entry提供了 K getKey() V getValue() 方法

  5. import java.util.HashMap;
    import java.util.Map;
    import java.util.Set;
    
    public class MAPSource {
        public static void main(String[] args) {
            Map map = new HashMap();
            map.put("no1", "xyy");
            map.put("no2", "myy");
    
            Set set = map.entrySet();
            System.out.println(set.getClass());//HashMap$EntrySet
    
            for (Object obj : set) {
    //            System.out.println(obj.getClass());  //class java.util.HashMap$Node
    //            为了从HashMap$Node取出k-v
    //            先做一个向下转型
                Map.Entry entry = (Map.Entry) obj;
                System.out.println(entry.getKey()+"--"+entry.getValue());
            }
    
        }
    }
    /*
    class java.util.HashMap$EntrySet
    no2--myy
    no1--xyy
    
    1. 在这里插入图片描述

    2. table表(数组+链表+红黑树) 为了方便管理在底层做处理,将每一个Node封装成一个Entry,再把Entry放到集合EntrySet中

常用方法
import java.util.HashMap;
import java.util.Map;

public class MAPSource {
    public static void main(String[] args) {
        Map map = new HashMap();
        map.put("no1", "xyy");
        map.put("no2", "myy");
        map.put("no3", "fyy");
        map.put("no4", "nyy");
        map.put("no5", "htl");
        System.out.println(map);

//        remove()  根据键删除映射关系
        map.remove("no1");
        System.out.println(map);

//        get() 根据键获取值
        Object val = map.get("no2");
        System.out.println(val);

//        size() 获取当前有多少对
        System.out.println(map.size());

//        isEmpty() 判断为空
        System.out.println(map.isEmpty());
        
        //修改
        map.put("no5","lyy");

//        clear() 清空
        map.clear();
        System.out.println(map);

//        containsKey() 查找键是否存在
        System.out.println(map.containsKey("no1"));


    }
}
/*
{no2=myy, no1=xyy, no4=nyy, no3=fyy, no5=htl}
{no2=myy, no4=nyy, no3=fyy, no5=htl}
myy
4
false
{}
false
遍历方法
import java.util.*;

@SuppressWarnings({"all"})
public class Example {
    public static void main(String[] args) {
        Map map = new HashMap();
        map.put("no1", "xyy");
        map.put("no2", "myy");
        map.put("no3", "fyy");
        map.put("no4", "nyy");
        map.put("no5", "htl");


//        第一组先取出所有的key 通过key,去除相应的value
        Set keyset = map.keySet();
//        增强for
        for (Object key :keyset) {
            System.out.println(key+"--"+map.get(key));
        }

//        迭代器
        Iterator iterator = keyset.iterator();
        while (iterator.hasNext()){
            Object key = iterator.next();
            System.out.println(key+"--"+map.get(key));
        }

//        第二组,把所有values取出来
        Collection values = map.values();
//        可以使用Collection使用的遍历方法
        System.out.println("取出所有value");
        for (Object o :values) {
            System.out.println(o);
        }
        System.out.println("取出所有value");
        for (Iterator iterator1 = values.iterator(); iterator1.hasNext();
             ){
            Object obj=iterator1.next();
            System.out.println(obj);
        }
        System.out.println("取出所有value");
        Iterator iterator1 = values.iterator();
        while(iterator1.hasNext()){
            Object obj = iterator1.next();
            System.out.println(obj);
        }
        
//        第三组,通过EntrySet
        Set entrySet = map.entrySet();
        for (Object entry: entrySet) {
//            将entry对象转成Map.Entry
            Map.Entry entry1 = (Map.Entry)entry;
            System.out.println(entry1.getKey()+"--"+entry1.getValue());
        }
        
//        迭代器
        Iterator iterator2 = entrySet.iterator();
        while (iterator2.hasNext()){
            Object next = iterator2.next();  //此时next为HashMap$Node类
                                             //实现-》Map.Entry(getKey,getValue)
            Map.Entry entry = (Map.Entry) next;
            System.out.println(entry.getKey()+"--"+entry.getValue());
        }


    }

}

练习
package Map接口.练习;

import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;

@SuppressWarnings({"all"})
public class Example {
    public static void main(String[] args) {
        Map map = new HashMap();
        Employee e1 = new Employee(250,"小明",19000);
        Employee e2 = new Employee(251,"小红",17000);
        Employee e3 = new Employee(252,"小蓝",20000);
        map.put("250",e1);
        map.put("251",e2);
        map.put("252",e3);

        Set set = map.keySet();
        System.out.println("第一种");
        for (Object key :set) {
            Employee emp = (Employee)map.get(key);
            if(emp.getSalary()>=19000){
                System.out.println(key+"--"+map.get(key));
            }
        }

        System.out.println("第二种");
        Set entrySet = map.entrySet();
        Iterator iterator = entrySet.iterator();
        while (iterator.hasNext()){
            Map.Entry entry = (Map.Entry)iterator.next(); 
            Employee emp = (Employee) entry;
            if (emp.getSalary()>=19000){
                System.out.println(entry.getKey()+"--"+entry.getValue());
            }
        }


    }
}

class Employee{
    private  int id;
    private  String name;
    private  int salary;

    public Employee(int id, String name, int salary) {
        this.id = id;
        this.name = name;
        this.salary = salary;
    }

    public int getId() {
        return id;
    }

    public String getName() {
        return name;
    }

    public int getSalary() {
        return salary;
    }

    @Override
    public String toString() {
        return "id:"+id+" name:"+name+"salary:"+salary;
    }
}
/*
第一种
250--id:250 name:小明salary:19000
252--id:252 name:小蓝salary:20000
第二种
250--id:250 name:小明salary:19000
252--id:252 name:小蓝salary:20000

(2)HashMap类

介绍
  1. 是Map接口使用频率最高的实现类
  2. 以key的hash和equals判断是否可以添加
  3. 没有实现同步,所以是线程不安全的
  4. 与hashset一样,不保证顺序,底层是hash表的方式存储(数组+链表+红黑树)
底层机制

可能一条是链表,而另一条是红黑树
在这里插入图片描述

扩容机制和hashSet完全一样
在这里插入图片描述

(3)HashTable类

介绍
  1. 存放的元素是键值对:即k-v

  2. hashtable的键和值都不能为null,否则会抛出NullPointerException异常

  3. 使用方法基本和HashMap一样

  4. HashTable线程安全(synchronized),HashMap是线程不安全的

底层
  1. 底层有数组HashTable$Entry[],初始化大小为11

  2. threshold(临界值) 8= 11*0.75

  3. 按照自己的扩容(有自己的扩容机制

  4. 满足 if (count >= threshold) ,则执行rehash()方法

  5. rehash()方法中,进行扩容的原理: int newCapacity = (oldCapacity << 1) + 1; 最终扩容到语句 Entry<?,?>[] newMap = new Entry<?,?>[newCapacity];

    
    
    //添加元素
    
     private void addEntry(int hash, K key, V value, int index) {
            Entry<?,?> tab[] = table;
            if (count >= threshold) {
                // Rehash the table if the threshold is exceeded
                rehash();
    
                tab = table;
                hash = key.hashCode();
                index = (hash & 0x7FFFFFFF) % tab.length;
            }
    
            // Creates the new entry.
            @SuppressWarnings("unchecked")
            Entry<K,V> e = (Entry<K,V>) tab[index];
            tab[index] = new Entry<>(hash, key, value, e);
            count++;
            modCount++;
        }
    
    
    //扩容进入rehash()
    protected void rehash() {
            int oldCapacity = table.length;
            Entry<?,?>[] oldMap = table;
    
            // overflow-conscious code
            int newCapacity = (oldCapacity << 1) + 1;
            if (newCapacity - MAX_ARRAY_SIZE > 0) {
                if (oldCapacity == MAX_ARRAY_SIZE)
                    // Keep running with MAX_ARRAY_SIZE buckets
                    return;
                newCapacity = MAX_ARRAY_SIZE;
            }
            Entry<?,?>[] newMap = new Entry<?,?>[newCapacity];
    
            modCount++;
            threshold = (int)Math.min(newCapacity * loadFactor, MAX_ARRAY_SIZE + 1);
            table = newMap;
    
            for (int i = oldCapacity ; i-- > 0 ;) {
                for (Entry<K,V> old = (Entry<K,V>)oldMap[i] ; old != null ; ) {
                    Entry<K,V> e = old;
                    old = old.next;
    
                    int index = (e.hash & 0x7FFFFFFF) % newCapacity;
                    e.next = (Entry<K,V>)newMap[index];
                    newMap[index] = e;
                }
            }
        }
    
    

    (4)HashMap和HashTable对比

在这里插入图片描述

(4)Properties类

介绍
  1. Properties 类继承Hashtable类并实现Map接口,也是键值对形存储数据
  2. 特点和Hashtable类似
  3. Properities可用于从xxx.properties文件中,加载到Properties对象,并进行读取和修改
  4. 工作后xxx.properties文件通常作为配置文件,

(5)TreeMap

TreeSet底层就是TreeMap

7.如何选择集合实现类

  1. 先判断存储类型(一组对象或者一组键值对)
  2. 一组对象[单列]:Collection接口

​ 允许重复:List:

​ 增删多:LinkedList(底层维护一个双向链表

​ 改查多:ArrayList(底层维护Object类型的数组

​ 不允许重复:

​ 无序:HashSet(底层是HashMap,维护了一个hash表(数组+链表+红黑树)

​ 排序:TreeSet

​ 插入和取出顺序一致:LinkedHashSet(维护数组+双向链表

  1. 一组键值对[双列]

    ​ 键无序:HashMap(底层是Hash表,数组+链表+红黑树(jdk7,是数组+链表

    ​ 键排序:TreeMap

    ​ 键插入和取出顺序一致:LinkeHashMap

    ​ 读取文件:properties

8.Collections工具类

  1. Collectionsshi一个操作Set,List和Map等集合的工具类
  2. 提供了一系列静态的方法对集合元素进行排序,查询和修改
  3. 第一组
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;

@SuppressWarnings({"all"})
public class Methods {
    public static void main(String[] args) {
        ArrayList list = new ArrayList();
        list.add(1);
        list.add(2);
        list.add(3);
        list.add(4);
        list.add(5);
        list.add(6);
        System.out.println(list);
//        reverse(list)  反转
        Collections.reverse(list);
        System.out.println(list);

//        shuffle(list) 随机排序
        Collections.shuffle(list);
        System.out.println(list);

//        sort(list) 排序
        Collections.sort(list);
        System.out.println(list);


//        sort(list, Comparator) 根据Comparator对list进行排序
        Collections.sort(list, new Comparator(){
            @Override
            public int compare(Object o1, Object o2) {
                return (int)o2 - (int)o1;
            }
        });
        System.out.println(list);

//        swap(list, x, y)对x和y位置进行交换
        Collections.swap(list,1,2);
        System.out.println(list);


    }
}
/*
[1, 2, 3, 4, 5, 6]
[6, 5, 4, 3, 2, 1]
[1, 6, 3, 4, 5, 2]
[1, 2, 3, 4, 5, 6]
[6, 5, 4, 3, 2, 1]
[6, 4, 5, 3, 2, 1]

  1. 第二组 查询和修改
package Collections工具类;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

@SuppressWarnings({"all"})
public class Methods2 {
    public static void main(String[] args) {
        ArrayList list = new ArrayList();
        list.add(1);
        list.add(2);
        list.add(3);
        list.add(4);
        list.add(5);
        list.add(6);

//        Object max(list) 返回自然排序下最大值
        System.out.println(Collections.max(list));

//        Object max(list, Comparator) 返回指定排序下最大值
        System.out.println(Collections.max(list, new Comparator() {
            @Override
            public int compare(Object o1, Object o2) {
                return (int)o2 - (int)o1;
            }
        }));

//        Object min(list) 返回自然排序下最小值
        System.out.println(Collections.min(list));

//        Object max(list, Comparator) 返回指定排序下最小值
        System.out.println(Collections.min(list, new Comparator() {
            @Override
            public int compare(Object o1, Object o2) {
                return (int)o2 - (int)o1;
            }
        }));

        list.add(2);
//        int frequency(Collection, Object)返回指定元素出现的次数
        System.out.println(Collections.frequency(list,2));

        List list1 = new ArrayList();
        list1.add(8);
        list1.add(9);

//        copy(dest, src) 将src中元素复制到dest
        // 为了要完成一个完整的拷贝,要保证dest的大小大于src
        Collections.copy(list,list1);
        System.out.println(list);

//        boolean replaceAll(list, Object oldVal, Object newVal) 使用新值替换list对象的旧值
        Collections.replaceAll(list,2,10);
        System.out.println(list);
    }
}
/*
6
1
1
6
2
[8, 9, 3, 4, 5, 6, 2]
[8, 9, 3, 4, 5, 6, 10]

9.扩容机制总结

在这里插入图片描述

泛型

1.泛型的好处

  1. 编译时,检查元素的类型,提高了安全性
  2. 减少了类型转换的次数,提高了效率
  3. 不再提示编译警告
public class generic1 {
    public static void main(String[] args) {
        ArrayList<Dog> arrayList = new ArrayList<Dog>();
        arrayList.add(new Dog("大黄",11));
        arrayList.add(new Dog("老黑",7));
        arrayList.add(new Dog("Tony",2));


//        arrayList.add(new Cat("马内",2));  报错,不能放Cat类型

        for (Dog dog : arrayList) {
            System.out.println(dog.getName()+"  "+dog.getAge());

        }

    }
}


class Dog{
    String name;
    int age;
    Dog(String name, int age){
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public int getAge() {
        return age;
    }
}

class Cat{
    String name;
    int age;
    Cat(String name, int age){
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public int getAge() {
        return age;
    }

}

2.泛型介绍

  1. 泛型又称参数化类型,jdk5.0之后解决数据类型安全性的问题

  2. 在类声明或实例化时只要制定好需要的具体的类型即可

  3. java泛型可以保证如果程序在编译时没有发出警告,运行时就不会产生ClassCastException异常,同时代码更加简洁,健壮

  4. 泛型的作用是:可以在类声明时通过一个标识表示类中某个属性的类型,或者是某个方法的返回值类型,或者是参数类型

    class Person<E>{
        E s; //E表示 s的数据类型,该类型在丁一Person对象时指定
        public Person(E s){ //E表示参数类型
            this.s = s;
        }
        
        public E f(){   // E表示返回类型
            return s; 
        }
    }
    

3.泛型使用

  1. 声明 interface 接口{} 和 class 类<K,V>{} (其中,KTV不代表值,而代表类型,任意字母都可以)

  2. 实例化 Lsit strList = new ArrayList() Iterator iterator = customor.iterator();

  3. import java.util.*;
    
    @SuppressWarnings({"all"})
    public class Example {
        public static void main(String[] args) {
            Student<String, Integer> hxm = new Student<>("HXM", 12);
            Student<String, Integer> yy = new Student<>("YY", 14);
            Student<String, Integer> yz = new Student<>("YZ", 11);
    
    //        HashSet<Student> students = new HashSet<>();
    //        students.add(hxm);
    //        students.add(yy);
    //        students.add(yz);
    //
    //        for (Student student :students) {
    //            System.out.println(student);
    //        }
    //
    //
    //        System.out.println("第二组");
    //        Iterator<Student> iterator = students.iterator();
    //        while (iterator.hasNext()){
    //            System.out.println(iterator.next());
    //        }
    
            HashMap<Integer,Student> map = new HashMap<Integer,Student>();
            map.put(1,hxm);
            map.put(2,yy);
            map.put(3,yz);
    
            for (Integer i :map.keySet()) {
                System.out.println(i+""+map.get(i));
            }
            System.out.println("第二组");
            Set<Map.Entry<Integer, Student>> entries = map.entrySet();
            Iterator<Map.Entry<Integer, Student>> iterator = entries.iterator();
            while (iterator.hasNext()){
                Map.Entry<Integer, Student> next = iterator.next();
                System.out.println(next.getKey()+""+next.getValue());
            }
    
    
        }
    }
    
    class  Student<E, V>{
        private  E name;
        private  V age;
        public  Student(E name, V age){
            this.name = name;
            this.age = age;
        }
    
        public E getName() {
            return name;
        }
    
        public void setName(E name) {
            this.name = name;
        }
    
        public V getAge() {
            return age;
        }
    
        public void setAge(V age) {
            this.age = age;
        }
    
        @Override
        public String toString() {
            return "name:"+name+ "   age:"+age;
        }
    }
    

4.使用细节

  1. T,E 只能是引用类型,不能是基本类型

  2. 在指定泛型具体类型后,可以传入该类型或者其子类类型

  3. 使用形式 List< Integer > list1 = new ArrayList< Integer >();

    实际开发中,经常简写

    List< Integer > list3 = new ArrayList<>();

  4. 如果是 List list1 = new ArrayList(); E默认为Object

5.案例

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

public class Example2 {
    public static void main(String[] args) {
        List<Employee> employees = new ArrayList<>();
        employees.add(new Employee("小红",1200,new Date(2000,10,9)));
        employees.add(new Employee("小蓝",1400,new Date(2001,1,8)));
        employees.add(new Employee("小红",1700,new Date(1999,4,5)));

        for (Employee employee :employees) {
            System.out.println(employee);
        }

        System.out.println("============");
        employees.sort( new Comparator<Employee>() {
            @Override
            public int compare(Employee o1, Employee o2) {
                if(o1.getName().equals(o2.getName())){
                    return  o1.getBirthday().compareTo(o2.getBirthday());
                }
                else {
                    return o1.getName().compareTo(o2.getName());
                }
            }
        });
        for (Employee employees1 :employees) {
            System.out.println(employees1);
        }


    }
}


class Employee{
    private String name;
    private int  salary;
    private Date birthday;

    public Employee(String name, int salary, Date birthday) {
        this.name = name;
        this.salary = salary;
        this.birthday = birthday;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getSalary() {
        return salary;
    }

    public void setSalary(int salary) {
        this.salary = salary;
    }

    public Date getBirthday() {
        return birthday;
    }

    public void setBirthday(Date birthday) {
        this.birthday = birthday;
    }

    @Override
    public String toString() {
        return "Employee{" +
                "name='" + name + '\'' +
                ", salary=" + salary +
                ", birthday=" + birthday +
                '}';
    }


}







class Date implements Comparable<Date>{
    private  int month;
    private  int year;
    private  int day;

    public int getMonth() {
        return month;
    }

    public void setMonth(int month) {
        this.month = month;
    }

    public int getYear() {
        return year;
    }

    public void setYear(int year) {
        this.year = year;
    }

    public int getDay() {
        return day;
    }

    public void setDay(int day) {
        this.day = day;
    }

    public Date(int year, int month, int day) {
        this.month = month;
        this.year = year;
        this.day = day;
    }

    @Override
    public String toString() {
        return year+"-"+month+"-"+day;
    }

    @Override
    public int compareTo(Date o) {
        int yearMinus = year - o.year;
        if (yearMinus!=0){
            return yearMinus;
        }
        int monthMinus = month - o.month;
        if (monthMinus!=0){
            return monthMinus;
        }
        return  day - o.day;
    }
}

6.自定义泛型

自定义泛型类

数组不能初始化(数组在new时,不能确定T的类型,就无法在内存开空间

静态属性、静态方法不能使用泛型(静态在类加载时,对象还没有创建,不能确定T的类型,jvm无法完成初始化

创建对象时没有指定则默认为Object

class  Student<E, V>{
    private  E name;
    private  V age;
    public  Student(E name, V age){
        this.name = name;
        this.age = age;
    }

    public E getName() {
        return name;
    }

    public void setName(E name) {
        this.name = name;
    }

    public V getAge() {
        return age;
    }

    public void setAge(V age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "name:"+name+ "   age:"+age;
    }
}

自定义泛型接口

interface 接口名<T,R…>{}

接口中,静态成员也不能使用泛型

泛型接口的类型,在继承接口和实现接口时确定

没有指定类型默认为Object

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

    }
}

class IB implements Iusb<Integer,Double>{

    @Override
    public Double get(Integer integer) {
        return null;
    }

    @Override
    public void hi(Double aDouble) {

    }

    @Override
    public void run(Double r1, Double r2, Integer u1, Integer u2) {

    }
}

class IA implements Iusb2{
    @Override
    public Integer get(String s) {
        return null;
    }

    @Override
    public void hi(Integer integer) {

    }

    @Override
    public void run(Integer r1, Integer r2, String u1, String u2) {

    }
}
interface  Iusb2 extends  Iusb<String ,Integer>{

}
interface  Iusb<U,R>{
    //普通方法,可以使用接口泛型
    R get(U u);

//    U name; 报错

    void hi(R r);

    void run(R r1, R r2, U u1, U u2);

//    使用默认方法可以使用泛型
    default R method(R r){
        return  r;
    }
}


自定义泛型方法

泛型方法,可以定义在普通类中,也可以定义在泛型类中

public void hi(E e){} 不是泛型方法,而是方法使用了类定义的泛型

泛型方法,可以使用类声明的泛型,也可以使用自己声明的泛型

public class GenericM {
    public static void main(String[] args) {
        Car car = new Car();
        //当调用方法时,传入参数,编译器会确定对应的类型
        car.fly("宝马", 2);
        System.out.println("===========");
        car.fly(4.2, "大黄");

    }
}

class Car{
    public void run(){ } // 普通方法

    public  <T, R> void fly(T t, R r){
        System.out.println(t.getClass());
        System.out.println(r.getClass());
    } //泛型方法
}

class Plane<T, R>{
    public void run(){ } // 普通方法

    public  <U, V> void fly(U u, V v){} //泛型方法
    public  <U, V> void walk(U u, V v,T t, R r){} //既使用自己的泛型,也使用类定义的泛型
}

7.泛型的继承和通配符

泛型不具备继承性 List< Object > list = new ArrayList(); (不可以

< ? > 支持任意泛型类型

< ? extends A> 支持A类以及A的子类,规定了泛型的上限

< ? super A> 支持A类以及A的父类,规定了泛型下限

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

public class Generic3 {
    public static void main(String[] args) {
        A a = new A();
        B b = new B();
        C c = new C();
        ArrayList<Object> list1 = new ArrayList<>();
        ArrayList<String> list2 = new ArrayList<>();
        ArrayList<A> list3 = new ArrayList<>();
        ArrayList<B> list4 = new ArrayList<>();
        ArrayList<C> list5 = new ArrayList<>();

        printCollection1(list1);
        printCollection1(list2);
        printCollection1(list3);
        printCollection1(list4);
        printCollection1(list5);

//        printCollection2(list1);  //错误
//        printCollection2(list2);  //错误
        printCollection2(list3);
        printCollection2(list4);
        printCollection2(list5);

        printCollection3(list1);
//        printCollection3(list2);  //错误
        printCollection3(list3);
//        printCollection3(list4);  //错误
//        printCollection3(list5);  //错误

        

    }

    public  static  void printCollection1(List<?> c){}
    
//    接受A 和A的子类
    public  static  void printCollection2(List<? extends  A> c){}
    
//    接受A 和A的父类
    public  static  void printCollection3(List<? super  A > c){}
}

class A{}

class B extends A {}

class C extends  B{}

8.Junit测试框架

package Junit_;

import org.junit.jupiter.api.Test;

public class Junit_ {
    public static void main(String[] args) {
//        传统方法
//        new Junit_().m1();
//        new Junit_().m2();
    }

    @Test   //alt + enter  选5...就可以单独测试方法
    public void m1(){
        System.out.println("m1被调用");
    }

    @Test
    public void m2(){
        System.out.println("m2被调用");
    }
}

输入输出流

1.文件

文件流: 文件在程序中是以流的形式来操作的

在这里插入图片描述

在这里插入图片描述

流:数据在数据源(文件)和程序(内存)之间经理的路径

输入流:数据从数据源(文件)到程序(内存)的路径

输出流:数据从程序(内存)到数据源(文件)的路径

2. File类

File(String path):根据路径构建
File(String path, String name):根据父目录+子路径构建
File(File dir, String name):根据父目录文件+子路径构建

● File类是java.io包中很重要的一个类;

● File类的对象可以表示文件,还可以表示目录(文件夹),在程序中一个File类对象可以代表一个文件或目录;

● File对象可以对文件或目录的属性进行操作,如:文件名、最后修改期、文件大小等;

● File对象无法操作文件的具体数据,即不能直接对文件进行读/写操作。

package File_;

import org.junit.jupiter.api.Test;

import java.io.File;
import java.io.IOException;

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

    }

    @Test
    public  void creat1(){
        String path = "E:\\newFile1.txt";
        File file1 = new File(path);
        //只在内存中建立了对象还没有和硬盘发生联系
//       只有执行creatNewFile() 方法才能真正在硬盘中创建文件
        try {
            file1.createNewFile();
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    @Test
    public void creat2(){
        File parentFile = new File("E:\\");
        String fileName = "newFile2.txt";
        File file2 = new File(parentFile,fileName);

        try {
            file2.createNewFile();
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    @Test
    public void creat3(){
        String parentPath = "E:\\";
        String fileName = "newFile3.txt";
        File file3 = new File(parentPath,fileName);

        try {
            file3.createNewFile();
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
}


常用方法

package File_;

import org.junit.jupiter.api.Test;

import java.io.File;

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

    }

    @Test
    public void  info(){
        File file = new File("E:\\newFile1.txt");

//        getName() 得到文件名字
        System.out.println("文件名字"+file.getName());
//        getAbsolutePath() 得到绝对路径
        System.out.println("文件绝对路径"+file.getAbsoluteFile());
//        getParent()得到父级目录
        System.out.println("文件父级目录"+file.getParent());
//        length()得到文件大小,字节   utf-8 英文字母一个字节,中文汉字三个字节
        System.out.println("文件大小"+file.length());
//        exists() 是否存在
        System.out.println("是否存在"+file.exists());
//        isDirectory() 是否目录
        System.out.println("是否目录"+file.isDirectory());


    }
}
/*
文件名字newFile1.txt
文件绝对路径E:\newFile1.txt
文件父级目录E:\
文件大小14
是否存在true
是否目录false

对目录进行操作

package File_;

import java.io.File;

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

    }
    public void m1(){
        String directoryPath = "E:\\demo";
        File file = new File(directoryPath);

        if(file.exists()){
            if(file.delete()){
                System.out.println("删除成功");
            }
            else{
                System.out.println("删除失败");
            }
        }
        else {
            System.out.println("不存在");
        }
    }


    public void m2(){
        String directoryPath = "E:\\demo\\a\\b\\c";
        File file = new File(directoryPath);

        if(file.exists()){
            System.out.println("存在");
        }
        else {
            //创建多级目录用mkdirs(),单级目录用mkdir()
            if (file.mkdirs()){
                System.out.println("创建成功");
            }
            else{
                System.out.println("创建失败");
            }
        }
    }
}

3.IO流原理及流的分类

原理

  1. I/O 是input/output缩写,IO技术是非常使用的技术,用于处理数据传输,如读写文件,网络通讯
  2. Java程序中,对于数据的输入输出操作以流(stream)的方式进行
  3. java.io包下提供了各种”流“类和接口,以获取不同种类的数据,并通过方法输入或输出数据
  4. 输入(input)读取外部数据(磁盘,光盘、网络、数据库等存储设备的数据)到程序(内存)中

分类

  1. 按操作数据单位不同分为:字节流(8位)和字符流(字符对应字节,要看编码) ,(字符流效率高<操作文本文件>,字节流操作二进制文件<声音,视频>无损

  2. 按数据流的流向:输入输出流

  3. 按流的角色:节点流,处理流/包装流

    在这里插入图片描述

  4. InputStream和OutputStream,Reader和Writer,都是抽象类,IO流类都是以上四个抽象类派生的。在这里插入图片描述

流VS文件

在这里插入图片描述

流相当于外卖小哥,帮助我们传输文件

4.文件字节流

InputStream

在这里插入图片描述

FileInputStream

细节在代码注释

package InputStream_;

import File_.File_;
import org.junit.jupiter.api.Test;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;

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

    }
    @Test
    public void  readFile01(){
        String filePath = "E:\\newFile1.txt";
        int readData = 0;
        FileInputStream fileInputStream = null;
        try {
//            创建FileInputStream对象用于读取文件
            fileInputStream= new FileInputStream(filePath);
//            返回-1表示读完
            while ((readData = fileInputStream.read())!=-1){
                System.out.print((char)readData);
//                读取中文必然出现乱码, 读取字节,而中文由三个字节组成
            }

        } catch (IOException e) {
            throw new RuntimeException(e);
        }finally {
            try {
                fileInputStream.close();
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }


    }
//  使用read(byte[] b)读取,提高效率
    @Test
    public void  readFile02(){
        String filePath = "E:\\newFile1.txt";
        int readLength= 0;
        byte[] buf = new byte[8];
        FileInputStream fileInputStream = null;
        try {
//            创建FileInputStream对象用于读取文件
            fileInputStream= new FileInputStream(filePath);

//            如果读取正常,返回实际读取字节数
//            返回-1表示读完
            while ((readLength=fileInputStream.read(buf))!=-1){
                String str = new String(buf,0,readLength);
                System.out.println(str);
            }

        } catch (IOException e) {
            throw new RuntimeException(e);
        }finally {
            try {
                fileInputStream.close();
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }


    }
}

OutputStream

在这里插入图片描述

FileOutputStream

两个构造器: new FileOutputStream(filePath); //每执行一次,从头开始写

​ new FileOutputStream(filePath,boolean);//添加到原有内容的后面

package OutputStream_;

import File_.File_;
import org.junit.jupiter.api.Test;

import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;

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

    }

    @Test
    public void write(){
//        如果文件不存在,则创建文件
        String filePath = "E:\\newFile2.txt";
        FileOutputStream fileOutputStream = null;
        try {
            fileOutputStream = new FileOutputStream(filePath,true);
//            写入单个字符
            fileOutputStream.write('a');
            fileOutputStream.write('\n');
//            写入字符串
            String str = "Hello, World";
//            str.getBytes() 把字符串转为字节数组
            fileOutputStream.write(str.getBytes());

//            write(byte[] b, int off, int len)
            fileOutputStream.write('\n');
            fileOutputStream.write(str.getBytes(),0,3);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }finally {
            try {
                fileOutputStream.close();
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }
    }
}

文件拷贝

package OutputStream_;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;

public class FileCopy {
    public static void main(String[] args) {
//        创建输入流,将文件读入到程序
//        创建输出流,将文件数据,写入指定文件

        String srcfilePath = "E:\\photo.webp";
        String destfilePath = "E:\\copytPhoto.webp";
        FileInputStream fileInputStream = null;
        FileOutputStream fileOutputStream = null;

        try {
            fileInputStream = new FileInputStream(srcfilePath);
            fileOutputStream = new FileOutputStream(destfilePath);
            byte[] buf = new byte[1024];
            int len = 0;
            while ((len=fileInputStream.read(buf))!=-1){
                fileOutputStream.write(buf);//一定要用这个方法
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        }finally {
            if(fileInputStream!=null){
                try {
                    fileInputStream.close();
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
            }
            if (fileOutputStream!=null){
                try {
                    fileOutputStream.close();
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
            }
        }
    }
}

5.文件字符流

FileReader

在这里插入图片描述

  1. new FileReader(File/String)

  2. read:每次读取单个字符,返回该字符,如果到文件末尾返回-1

  3. read(char []) 批量读取多个字符到数组,返回读取的字符数,末尾返回01

  4. new String(char []) 将char[] 转换成String

  5. new String(char[] , off, len):将char[]的指定部分转换成String

  6. package FileReader_;
    
    import org.junit.jupiter.api.Test;
    
    import java.io.FileNotFoundException;
    import java.io.FileReader;
    import java.io.IOException;
    
    public class FileReader_ {
        public static void main(String[] args) {
    
        }
    
        @Test
        public void read01(){
            String filePath = "E:\\newFile3.txt";
            FileReader fileReader = null;
            int a = 0;
            try {
                fileReader = new FileReader(filePath);
                while ((a=fileReader.read())!=-1){
                    System.out.print((char)a);
                }
    
    
            } catch (IOException e) {
                throw new RuntimeException(e);
            }finally {
                try {
                    fileReader.close();
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
            }
        }
    
        @Test
        public void read02(){
            String filePath = "E:\\newFile3.txt";
            FileReader fileReader = null;
            int readlen = 0;
            char buf[] = new char[8];
            try {
                fileReader = new FileReader(filePath);
    //            返回实际读取的字符数
                while ((readlen=fileReader.read(buf))!=-1){
                    System.out.print(buf);
                }
    
    
            } catch (IOException e) {
                throw new RuntimeException(e);
            }finally {
                try {
                    fileReader.close();
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
            }
        }
    
    
    }
    
    
    

FileWriter

在这里插入图片描述

  1. new FileWriter(File/String):覆盖模式,
  2. new FileWriter(File/String, true) 追加模式
  3. write(int) 写入单个字符
  4. write(char[]) 写入指定数组
  5. write(char[], off, len):写入指定数组的指定部分
  6. write(string) :写入整个字符
  7. write(string, off, len)写入字符串指定部分
  8. String类:toCharArray:将String转换成char[]
  9. FileWriter使用后,必须要关闭(close)或刷新(flush),否则写入不到指定的文件
package FileWriter_;

import org.junit.jupiter.api.Test;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;

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

    }

    @Test
    public void writer01(){
        String filePath = "E:\\note.txt";
        FileWriter fileWriter = null;
        char[] chars = {'a','b','c'};
        try {
            fileWriter = new FileWriter(filePath);
//            1. write(int) 写入单个字符
            fileWriter.write('H');
//            2. write(char[]) 写入指定数组
            fileWriter.write(chars);
//            3. write(char[], off, len):写入指定数组的指定部分
            fileWriter.write("喜羊羊爱吃草".toCharArray(),0,4);
//            4. write(string) :写入整个字符
            fileWriter.write("暖羊羊和灰太狼");
//            5. write(string, off, len)写入字符串指定部分
            fileWriter.write("北京欢迎你~~~",0,3);



        } catch (IOException e) {
            throw new RuntimeException(e);
        }finally {
            try {
                fileWriter.close();
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }
    }
}

6.节点流和处理流

在这里插入图片描述

节点流

节点流可以从一个特定的数据源读写数据,如FileReader(灵活性差,效率低,跟数据直接相关

处理流

也叫包装流,是”连接“在已存在的流(节点流或处理流)之上,为程序提供了更强大的读写功能,也更加灵活

例:

BufferedReader类中,有属性Reader,即可以封装一个节点流,该节点流可以是任意的,只要是Reader子类( 有构造器new BufferedReader(Reader readre);

BufferedWriter类中,有属性Writer,即可以封装一个节点流,该节点流可以是任意的,只要是Reader子类

节点流和处理流区别和联系

  1. 节点流是底层流/低级流,直接跟数据源相接
  2. 处理流包装节点流,可以消除不同节点流的实现差异,也可以提供更方便的方法来完成输入输出(源码理解
  3. 处理流对节点进行包装,使用了修饰器设计模式,不会直接与数据相连(模拟修饰器设计模式
  4. 处理流的功能:

​ 性能的提高:主要增加缓冲的方式来提高输入输出的效率

​ 操作的便捷:处理流可能提供了一系列便捷的方法来依次输入输出大批量的数据, 使用更加灵活方便

修饰器模拟:

//Test.java
public class Test {
    public static void main(String[] args) {
        BufferedReader_ bufferedReader_ = new BufferedReader_(new FileReader());
        bufferedReader_.readFiles(5);

//        这次希望对Pipe读取
        BufferedReader_ bufferedReader_1 = new BufferedReader_(new PipeReader());
        bufferedReader_1.readPipe(5);
    }
}



//Reader_.java
public abstract class Reader_ {
    public void readFile(){};
    public void readPipe(){};
}

//PipeReader.java
public class PipeReader extends  Reader_{

    public void readPipe(){
        System.out.println("读取管道");
    }
}

//FileReader.java
public class FileReader extends Reader_{

    public void readFile(){
        System.out.println("读取文件");
    }
}

//BufferedReader_.java
//做成处理流
public class BufferedReader_  extends Reader_{
    private Reader_ reader;

    BufferedReader_(Reader_ reader_){
        reader = reader_;
    }

    public void readFiles(int num){
        for (int i = 0; i<num; i++){
            reader.readFile();
        }
    }

    public void readPipe(int num){
        for(int i = 0; i < num;i++){
            reader.readPipe();
        }
    }

}

BufferedReader

字符流,按照字符读取数据,关闭时只关闭外层流就可以

package FileReader_;

import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileReader;

public class BufferedReader_ {
    public static void main(String[] args) throws Exception {
        String filePath = "E:\\newFile3.txt";
        BufferedReader bufferedReader = null;
        bufferedReader = new BufferedReader(new FileReader(filePath));
        String line;
//        按行读取,效率高,返回null时表示读取完毕
        while((line = bufferedReader.readLine())!=null){
            System.out.println(line);
        }

//        只需关闭外层流,即bufferedReader即可,底层会自动关闭FileReader
        bufferedReader.close();
    }
}


/*
public void close() throws IOException {
        synchronized (lock) {
            if (in == null)
                return;
            try {
                in.close();   //in就是传进来的内层流
            } finally {
                in = null;
                cb = null;
            }
        }

BufferedWriter

字符流,按照字符读取数据, 关闭时只关闭外层流就可以

package FileWriter_;

import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;

public class BufferedWriter_ {
    public static void main(String[] args) throws IOException {
        String  filePath = "E:\\ok.txt";
        BufferedWriter bufferedWriter = null;
//        想要追加写 FileWriter(String, true)
        bufferedWriter = new BufferedWriter(new FileWriter(filePath));
        bufferedWriter.write("hello,喜羊羊");
        bufferedWriter.newLine();   //插入一个和系统相关的换行
        bufferedWriter.write("hello,喜羊羊");
        bufferedWriter.newLine();
        bufferedWriter.write("hello,喜羊羊");

        bufferedWriter.close();

    }
}

使用Buffered文件拷贝

package OutputStream_;

import java.io.*;

public class BufferedCopy {
    public static void main(String[] args) {
//        创建输入流,将文件读入到程序
//        创建输出流,将文件数据,写入指定文件

        String srcfilePath = "E:\\photo.webp";
        String destfilePath = "E:\\copytPhoto2.webp";
        BufferedInputStream bfi = null;
        BufferedOutputStream bfo = null;
        try {
             bfi = new BufferedInputStream(new FileInputStream(srcfilePath));
             bfo = new BufferedOutputStream(new FileOutputStream(destfilePath));
             byte[] buf= new byte[1024];
             int len = 0;
             while ((len=bfi.read(buf))!=-1){
                 bfo.write(buf);//一定要用这个方法
             }
        } catch (IOException ex) {
            throw new RuntimeException(ex);
        }finally {
            try {
                bfi.close();
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
            try {
                bfo.close();
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }
    }


}

7.对象处理流

介绍

  1. 序列化: 保存数据的值和数据类型

  2. 反序列化:恢复数据的值和数据类型

  3. 使用对象流必须保证对象是序列化的,且成员对象也必须是序列化

  4. 序列化是为了保证能把对象写入文件,并能正确度回到程序。

  5. 为了让对象序列化,需让对象实现Serializable(标记接口,没有方法,常用)或者Externalizable接口

  6. ObjectInputStream,ObjectoutputStream中有inputstream和outputstream成员变量,是修饰器类型。构造器

    ​ new ObjectInputStream(InputStream)

    ​ new ObjectOutputStream(OutputStream)

  7. 反序列化时,读取数据和存放数据一致

package ObjectStream_;

import java.io.*;

public class ObjectStream_ {
    public static void main(String[] args) throws IOException, ClassNotFoundException {
        String path = "E:\\objectFile.dat";
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(path));
        oos.writeInt(100);  //int -》 装箱成Integer(实现Serializable
        oos.writeBoolean(true);  //boolean -》装箱成Boolean(实现Serializable
        oos.writeChar('a'); //char -》 装箱成 Character(实现Serializable
        oos.writeUTF("喜羊羊"); //String
        oos.writeObject(new Dog("大黄",12));
        oos.close();

        ObjectInputStream ois = new ObjectInputStream(new FileInputStream(path));
        //读取顺序和存放顺序一致
        System.out.println(ois.readInt());
        System.out.println(ois.readBoolean());
        System.out.println(ois.readChar());
        System.out.println(ois.readUTF());
        Object dog = ois.readObject();
        System.out.println("运行类型:"+dog.getClass());
        System.out.println("dog信息:"+dog); //底层Object-》dog

//        如果需要调用Dog方法,需要向下转型
//        需要我们将Dog类的定义拷贝到可以引用的位置
        Dog dog1 = (Dog)dog;
        System.out.println("姓名"+dog1.getName());
        ois.close();
    }
}

class  Dog implements Serializable{
    String name;
    int age;

    public Dog(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 String toString() {
        return "Dog{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

注意

  1. 读取顺序一致
  2. serialVersionUID 序列化版本号,可以提高兼容性(当进行序列化时,不会因为添加东西而认为是新的类
  3. 默认将所有成员序列化,除了static或transient修饰的成员
  4. 序列化具有可继承性

对象克隆

//Example.java
public class Example {
	public static void main(String[] args)  {
		
		
		TV changhongTv  = new TV("changhong", 200);
		File file = new File("television.txt");
		try {
			FileOutputStream fileOut = new FileOutputStream(file);
			ObjectOutputStream objectOut = new ObjectOutputStream(fileOut);
			objectOut.writeObject(changhongTv);
			objectOut.close();
			
			FileInputStream fileIn = new FileInputStream(file);
			ObjectInputStream objectIn = new ObjectInputStream(fileIn);
			TV xinfei = (TV)objectIn.readObject();//此时xinfei和changhong内容一模一样
			objectIn.close();
//			xinfei.setName("心扉电视");
//			xinfei.setPrice(6666);
			
			System.out.println("changhong的名字"+changhongTv.getName());
			System.out.println("changhong的价格"+changhongTv.getPrice());
			System.out.println("xinfei的名字"+xinfei.getName());
			System.out.println("xinfei的名字"+xinfei.getPrice());
		} catch (Exception event) {
			// TODO: handle exception
		}
	}
}

//TV.java
import java.io.Serializable;

public class TV implements Serializable{
	private String name;
	private int price;
	public TV(String name, int price) {
		// TODO Auto-generated constructor stub
		this.name = name;
		this.price = price;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public int getPrice() {
		return price;
	}
	public void setPrice(int price) {
		this.price = price;
	}
	
	@Override
	public String toString() {
		// TODO Auto-generated method stub
		return "名字"+this.name+"价格"+this.price;
	}
	
}

8.标准输入输出流

system.in

System类的public final static InputStream in = null

System.in 编译类型 InputStream

System.in 运行类型 BufferedInputStream

表示标准输入(键盘

system.out

System类 public final static PrintStream out = null

编译类型 PrintStream

运行类型 PrintStream

表示标准输出(显示器

例:System.out.println(“xyy”) 相当于调用out类的println方法

9.转换流

字节流 《–》 字符流 (引文乱码问题,字符流不能指定编码格式

  1. InputStreamReader:Reader的子类,可以将InputStream(字节流)包装成Reader(字符流),构造器 new InputStreamReader(InputStream, Charset)
  2. OutputStreamReader:Writer的子类,实现将OutputStream(字节流)包装成Writer(字符流),构造器 new OutputStreamReader(OutputStream, Charset)
  3. 当处理纯文本数据,使用字符流效率更高,可以有效解决中文问题,建议将字节流转换成字符流
  4. 可以在使用时指定编码格式(比如utf-8,gdk,gb2312,ISO8859-1)

InputStreamReader

package InputStreamReader_;

import java.io.*;

public class InputStreamReader_ {
    public static void main(String[] args) throws IOException {
//        将FileInputStream转换成字符流,指定编码utf-8
        String path = "E:\\newFile1.txt";
        InputStreamReader gbk = new InputStreamReader(new FileInputStream(path), "utf-8");
//        把InputStreamReader转换成BufferedReader
        BufferedReader bufferedReader = new BufferedReader(gbk);
//        读取
        String s = bufferedReader.readLine();
        System.out.println(s);
//       关闭外层流
        bufferedReader.close();
    }
}

OutputStreamWriter

package OutputStreamWriter_;

import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;

public class OutputStreamWriter_ {
    public static void main(String[] args) throws IOException {
        String path = "E:\\myy.txt";
        OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream(path),"utf-8");
        osw.write("美羊羊");
        osw.close();
    }
}

10 文件锁

上锁

RandomAccessFile input = new RandomAccessFile(file, “rw”);
​ FileChannel channel = input.getChannel();
​ FileLock lock = channel.tryLock();

解锁:

​ lock.release();

import java.nio.channels.*;
import java.util.Scanner;
import java.io.*;

public class Example {
	@SuppressWarnings("resource")
	public static void main(String[] args) {
		File file = new File("C:\\Program Files\\Java\\Eclipse\\Project","xx.txt");
		Scanner scanner = new Scanner(System.in);
		try {
			RandomAccessFile input = new RandomAccessFile(file, "rw");
			FileChannel channel = input.getChannel();
			FileLock lock  = channel.tryLock();
			System.out.println("n行");
			while(scanner.hasNextInt()) {
				int m = scanner.nextInt();
				lock.release();
				
				for(int i = 1; i<=m;i++) {
					String string = input.readLine();
					System.out.println(string);
				}
				
				lock = channel.tryLock();
				System.out.println("n行");
			}
		} catch (IOException e) {
			// TODO:
            handle exception
		}
	}
}

11.打印流

只有输出流没有输入流

PrinterStream , 字节流, System.setOut()方法设置输出位置(默认为屏幕)

print()方法底层是write()方法

System.setOut(new PrintStream(path)); 
public static void setOut(PrintStream out) {
        checkIO();
        setOut0(out);  //setOut为本地方法
    }

PrintWriter 字符流

PrintWriter printWriter = new PrintWriter(System.out);//默认输出屏幕
        PrintWriter printWriter1 = new PrintWriter(new FileWriter("E:\\myy.txt"));//输出到指定路径
        printWriter1.write("hi,beijing");
        printWriter1.close();//flush+关闭流,才会将数据写入到文件。。

12.Properties类

在这里插入图片描述

  1. 专门用于读写配置文件的集合类 配置文件格式:键=值

  2. 键值对不需要有空格,值不需要用引号引起来,默认类型String

  3. 父类是hashtable,核心方法就是hashtable方法

常用方法:

  1. load:加载配置文件的键值对到Properties对象
  2. list:将数据显示到指定设备
  3. getProperty(key):根据键获取值
  4. setProperty(key,value):设置键值对到对象
  5. store:将Properties中的键值对存储到配置文件,在idea中,保存信息到配置文件,如果含有中文,会存储问unicode码
package Properties_;

import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.util.Properties;

public class Properties01 {
    public static void main(String[] args) throws IOException {
        Properties properties = new Properties();
        properties.load(new FileReader("src\\mysql.properties"));
        properties.list(System.out);
//

        System.out.println("========");
        String name = (String) properties.get("name");
        String password = (String) properties.get("password");
        System.out.println("用户名是"+name);
        System.out.println("密码是"+password);

        System.out.println("=========");
    }
}

package Properties_;

import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Properties;

public class Properties02 {
    public static void main(String[] args) throws IOException {
        Properties properties = new Properties();
        properties.setProperty("user","xyy");
        properties.setProperty("pwd","2021250");
        properties.setProperty("汤姆","250");
        
        properties.setProperty("user","nyy"); //相当于修改    						
        properties.store(new FileOutputStream("src\\mysql2.properties"),null); //null为注释,放在properties文件的最上方
    }
}
/*
pwd=2021250
user=xyy
\u6C64\u59C6=250      汤姆的Unicode码

多线程

创建线程两种方式

  1. 继承Thread类,重写run()方法(Thread类实现了Runnable接口
  2. 实现Runnable接口,重写run()方法

线程同步–synchronized

多线程编程中,一些敏感数据不允许被多个线程同时访问,此时就使用同步访问计数,保证数据在任何同一时刻,最多有一个线程访问,以保证数据完整性

//1
synchronized(对象){
    //需要被同步的代码
}
//2
public synchronized void m(String name){
    //需要被同步的代码
}

例: 展示创建线程两种方式 及 线程同步方法 及 互斥锁

package 多线程售票;

public class SellTickets {
	public static void main(String[] args) {
		//01
//		SellTickets01 sellTickets01 = new SellTickets01();
//		SellTickets01 sellTickets02 = new SellTickets01();
//		SellTickets01 sellTickets03 = new SellTickets01();
//		sellTickets01.start();
//		sellTickets02.start();
//		sellTickets03.start();
		
		//02
//		SellTickets02 sellTickets01 = new SellTickets02();
		
//		Thread thread1 = new Thread(sellTickets01);
//		Thread thread2 = new Thread(sellTickets01);
//		Thread thread3 = new Thread(sellTickets01);
//		thread1.start();
//		thread2.start();
//		thread3.start();
		
		//03
		SellTickets03 sellTickets3 = new SellTickets03(); 
		new Thread(sellTickets3).start();//第 1 个线程-窗口 
		new Thread(sellTickets3).start();//第 2 个线程-窗口 
		new Thread(sellTickets3).start();//第 3 个线程-窗口
		
	}
}

class SellTickets01 extends Thread{
	private static int tickets = 100;
	 public void run() {
		while(true) {
			if(tickets < 0) {
				System.out.println("已售完");
				break;
			}
			try {
				Thread.sleep(500);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			System.out.println("窗口"+Thread.currentThread().getName()
					+"售出一张票,剩余票数"+(tickets--));
		}
	}
}

class SellTickets02 implements Runnable{
	private static int tickets = 100;
	public void run() {
		while(true) {
			if(tickets < 0) {
				System.out.println("已售完");
				break;
			}
			try {
				Thread.sleep(500);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			System.out.println("窗口"+Thread.currentThread().getName()
					+"售出一张票,剩余票数"+(tickets--));
		}
	}
}

class SellTickets03 implements Runnable{
	private static int tickets = 50;
	private static boolean loop = true;
	public  void sell() {
		synchronized (this) {
			if(tickets < 0) {
				System.out.println("已售完");
				loop = false;
				return;
			}
			try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			System.out.println("窗口"+Thread.currentThread().getName()
					+"售出一张票,剩余票数"+(tickets--));
		}
		
	}
	
	public void run() {
		while(loop) {
			try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			sell();
		}
	}
}

线程常用方法

setName 设置名称

getName 得到名称

start 线程开始执行

run 调用run方法

setPriority 设置线程优先级

getPriority 得到线程优先级

sleep 休眠

interrupt 中断线程 (中断休眠

线程中断例子

package Thread01;


public class 线程中断 {
	public static void main(String[] args) throws InterruptedException {
		Eat eat = new Eat();
		eat.setName("大黄");
		eat.setPriority(Thread.MIN_PRIORITY);
		eat.start();
		
		for(int i = 0; i < 5; i++) {
			Thread.sleep(1000);
			System.out.println("hi" + i);
		}
		
		eat.interrupt();//提前终止休眠
	}
}

class Eat extends Thread{
	@Override
	public void run() {
		while(true) {
			for(int i = 0; i<50; i++) {
				System.out.println(Thread.currentThread().getName()+"吃包子");
			}
			try {
				System.out.println(Thread.currentThread().getName()+"睡觉");
				Thread.sleep(10000);
				
			}catch (InterruptedException e) {
				// TODO: handle exception
				System.out.println(Thread.currentThread().getName()+"被interrupt");
			}
		}
		
	}
}

线程终止

1 线程完成任务自动退出

2 通过使用变量控制run方法推出停止线程,即通知方式

package Thread01;

import java.util.Iterator;

public class 线程终止 {
	public static void main(String[] args) {
		A a = new A();
		new Thread(a).start();
		for (int i = 0; i < 20 ;i++) {
			try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			System.out.println("main执行");
			if(i == 10) {
				a.setLoop(false);
			}
		}
	}
}

class A implements Runnable{
	private boolean loop = true;
	@Override
	public void run() {
		// TODO Auto-generated method stub
		while(loop) {
			try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			System.out.println("线程A正在运行");
		}
	}
	public void setLoop(boolean loop) {
		this.loop = loop;
	}
}

守护线程

将一个线程设为守护线程,当所有线程结束时该线程也结束

setDaemon() 方法设为守护线程

package 守护线程;

public class ProtectThread {
    public static void main(String[] args) throws InterruptedException {
        ProtectThread0 protectThread0 = new ProtectThread0();
        protectThread0.setDaemon(true);
        protectThread0.start();
        for(int i = 0; i < 20; i++) {
            Thread.sleep(1000);
            System.out.println("小兰在和小红聊天");
        }
    }
}


class ProtectThread0 extends Thread{
    public void run() {
        while(true) {
            try {
                Thread.sleep(500);
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            System.out.println("小明在和小红聊天");
        }
    }
}

线程插队

yield:线程的礼让,让出cup,让其他线程执行,但礼让事件不确定,所有不一定礼让成功

join:线程的插队,插队的线程一旦成功,则肯定先执行完插入的线程所有的任务

package Thread01;

import java.util.Iterator;

public class 线程插队 {
	public static void main(String[] args) throws InterruptedException {
		Hello hello = new Hello();
		Thread thread = new Thread(hello);
		thread.start();
		
		for(int i = 0; i < 10; i++) {
			try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			System.out.println("Hi");
			if(i == 5) {
				System.out.println("主线程让出");
//				join()方法
				
//				thread.join();
				
//				yield()方法,不一定成功
				Thread.yield();
			}
		}
	}
}

class Hello implements Runnable{
	@Override
	public void run() {
		// TODO Auto-generated method stub
		for(int i = 0; i < 10; i++) {
			try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			System.out.println("Hello");
		}
		
	
		
	}
}

互斥锁

  1. java语言中,引入对象互斥锁概念,保证共享数据操作的完整性
  2. 每个对象对应于一个可成为“互斥锁”的标记,这个标记来保证在任意时刻,只能有一个线程访问该对象
  3. 关键字synchronized来与对象的互斥锁联系,当某个对象用该关键字修饰时,表明该对象在任意时刻只能由一个线程访问(要求是同一个对象
  4. 同步的局限性:导致程序的执行效率降低
  5. 同步方法(非静态的)的锁可以是this,也可以是其他对象
  6. 同步方法(静态的)的锁为当前类本身

线程死锁

多个线程都占用了对方的锁资源,,但不肯相让,导致死锁

package Thread01;


public class 死锁 {
	public static void main(String[] args) {
		DeadLockDemo a = new DeadLockDemo(true);
		DeadLockDemo b = new DeadLockDemo(false);
		a.setName("A");
		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() {
		// TODO Auto-generated method stub
//		如果flag == true 线程A会先得到o1对象锁,然后尝试获取o2对象锁
//		如果得不到,就会Blocked
//		如果flag ==false 线程B会先得到o2对象锁,然后尝试获取o1对象锁
//		如果得不到,就会Blocked
		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");
					
				}
			}
		}
	}
}

释放锁

下面操作会释放锁

  1. 当前线程的同步方法、同步代码块执行结束(案例:上完厕所出来
  2. 当前线程在同步代码块、同步方法中遇到break,return(没有正常完事,经理叫他出来修bug,不得已出来
  3. 当前线程在同步代码块、同步方法中出现了未处理的Error或Exception,导致异常结束(没有正常完事,发现没带纸,不得已出来
  4. 当前线程在同步代码块、同步方法中执行了线程对象的wait()方法,当前线程暂停,并释放锁(没有正常完事,需要酝酿下,所以出来等会再进去

下面操作不会释放锁

  1. 线程执行同步代码块或同步方法时,调用sleep(), yield()方法暂停当前线程的执行,不会释放锁(上厕所太困了眯了一会
  2. 线程执行同步代码块时,其他线程调用了该线程的suspend()方法将该线程挂起
  3. 尽量避免suspend()和resume()来控制线程

线程的生命周期

getState() 方法可以查看线程状态
在这里插入图片描述
在这里插入图片描述

案例

在main方法中启动两个线程,第一个线程随即打印出1-100的数字,第二个线程输入Q结束第一个线程

package 线程例题;

import java.util.Scanner;

public class Example {
    public static void main(String[] args) {
        A a = new A();
        B b = new B(a);
        a.start();
        b.start();
    }
}

class A extends Thread{
    private boolean loop = true;
    public void run() {
        while(loop) {
            System.out.println((int)(Math.random()*100)+1);
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) { 
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
    }
    public void setLoop(boolean loop) {
        this.loop = loop;
    }
}

class B extends Thread{
    A a;
    Scanner scanner = new Scanner(System.in);
    public B(A a) {
        this.a = a;
    }
    public void run() {
        while(true) {
            while(true) {
                System.out.println("请输入字母(Q)结束线程");
                char key = scanner.next().toUpperCase().charAt(0);
                if( key == 'Q') {
                    a.setLoop(false);
                    System.out.println("B线程结束");
                    break;
                }
            }
        }
    }
}

反射

1. 反射机制

入门

根据配置文件指定信息,创建Cat对象并调用方法hi

通过外部文件,在不修改源码的情况下来控制程序,也符合设计模式的ocp原则(开闭原则)

反射是框架的灵魂

需要改变调用的方法只需改变外部文件(将method = hi 改为method=cry

package Reflection_;

import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Properties;

public class ReflectionProblem {
    public static void main(String[] args) throws IOException, ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException {
        Properties properties = new Properties();
        properties.load(new FileReader("src\\re.properties"));
        String classpath = properties.getProperty("classfullpath").toString();
        String methodName = properties.getProperty("method").toString();


//        使用反射机制解决问题
//        1.加载类,返回Class类型的对象cls
        Class cls = Class.forName(classpath);
//        2.通过cls得到加载的类Cat的实例对象
        Object obj = cls.newInstance();
        System.out.println("obj的运行类型"+obj.getClass());
//        3.通过cls得到加载的类Cat的methodName“hi”的方法对象
//        在反射机制中,把方法视为对象(万物皆对象)
        Method method = cls.getMethod(methodName);
//        通过method调用方法,即通过方法对象调用方法
        System.out.println("-============-");
        method.invoke(obj);  //方法.invoke(对象)
//        没有反射就没有框架

    }
}


package Reflection_;

public class Cat {
    private String name = "大黄";
    public  int age = 10;
    public Cat(){
        System.out.println("无参构造器");
    }
    public Cat(String name){
        System.out.println("有参构造器");
    }

    public void hi(){
        System.out.println("喵喵喵");
    }

    public void cry(){
        System.out.println("小猫哭了");
    }
}
/*
re.properties文件内容
classfullpath=Reflection_.Cat
method=cry

反射机制

  1. 反射机制允许程序在执行期借助于Reflection API取得任何类的内部信息(比如成员变量,构造器,成员方法)并能操作对象的属性及方法。反射在设计模式和框架底层会用到

  2. 加载完类之后,在堆中产生一个Class类型的对象(一个类只有一个Class对象),这个对象包含了类的完整结构信息,通过这个对象得到类的结构,

  3. 在这里插入图片描述

  4. 运行时判断任意一个对象所属的类

  5. 运行时构造任意一个类的对象

  6. 运行时得到任意一个类所具有的成员变量和方法

  7. 运行时调用一个对象的成员变量和方法

  8. 生成动态代理

反射相关类

这些类在java.lang.reflect包

java.lang.Class 代表一个类,Class对象表示某个类加载后在堆中的对象

java.lang.reflect.Method 代表类方法,Method对象表示某个类的方法

java.lang.reflect.Field 代表类的成员变量,Field对象表示某个类的成员变量

java.lang.reflect.Constructor 代表类的构造方法。Constructor表示类的构造器

 // java.lang.reflect.Constructor: 代表类的构造方法, Constructor 对象表示构造器
        Constructor constructor = cls.getConstructor();
        //()中可以指定构造器参数类型, 返回无参构造器
        System.out.println(constructor);
        //Cat()
        Constructor constructor2 = cls.getConstructor(String.class); //这里老师传入的 String.class 就是 String 类的 Class 对象
        System.out.println(constructor2);//Cat(String name)

优缺点

优点:可以动态创建和适用对象(也是框架底层核心),使用灵活,没有反射机制,框架就失去底层支撑

缺点:使用反射基本是解释执行,队速度有影响

优化

Method,Fiedl,Constructor对象都有setAccessible()方法

作用是启动和禁用访问安全检查的开关

参数值为true表示 反射的对象在使用时取消访问检查,提高反射效率。参数值为false则表示反射对象执行访问检查

package Reflection_;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class Reflection2 {
    public static void main(String[] args) throws ClassNotFoundException, InvocationTargetException, NoSuchMethodException, IllegalAccessException, InstantiationException {
        m1();
        m2();
        m3();
    }

    public static void m1() {
        Cat cat = new Cat();
        long starttime = System.currentTimeMillis();
        for (int i = 0; i < 10000000; i++) {
            cat.cry();
        }
        long endtime = System.currentTimeMillis();
        System.out.println("m1:" + (endtime - starttime));

    }

    public static void m2() throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException, InstantiationException {
        Class cls = Class.forName("Reflection_.Cat");
        Object obj = cls.newInstance();
        Method method = cls.getMethod("cry");

        long starttime = System.currentTimeMillis();
        for (int i = 0; i < 10000000; i++) {
            method.invoke(obj);
        }
        long endtime = System.currentTimeMillis();
        System.out.println("m2:" + (endtime - starttime));

    }

    //    反射优化 关闭访问检查
    public static void m3() throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException, InstantiationException {
        Class cls = Class.forName("Reflection_.Cat");
        Object obj = cls.newInstance();
        Method method = cls.getMethod("cry");
        method.setAccessible(true);
        long starttime = System.currentTimeMillis();
        for (int i = 0; i < 10000000; i++) {
            method.invoke(obj);
        }
        long endtime = System.currentTimeMillis();
        System.out.println("m3:" + (endtime - starttime));

    }

}
/*
无参构造器
m1:3
无参构造器
m2:109
无参构造器
m3:77

2.Class类

  1. Class也是类,继承Object类

  2. Class类对象不是new出来的,而是系统创建的

  3. 对于某个类的Class对象,在内存中只有一份,因为类只加载一次

  4. 每个类的实例都会记得自己是由哪个Class实例所生产

  5. 通过Class对象可以完整的得到一个类的完整结构,一系列API在这里插入图片描述

  6. Class对象是放在堆的

  7. 类的字节码二进制数据,是放在方法区的,有的地方称为类的元数据

常用方法

package Class_;

import Reflection_.Car;

import java.lang.reflect.Field;

public class Class02 {
    public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchFieldException {
        String classpath = "Reflection_.Car";
//        <?>表示不确定的Java类型
        Class<?> cls = Class.forName(classpath);
//        获得哪个类的Class对象
        System.out.println(cls);
//        获得运行类型
        System.out.println(cls.getClass());
//         得到包的名字
        System.out.println(cls.getPackageName());
//        得到全类名
        System.out.println(cls.getName());
//        创建对象实例
        Car car = (Car)cls.newInstance();
        System.out.println(car);
//        通过反射获得属性
        Field field = cls.getField("name");
        System.out.println(field.get(car));
//        通过反射给属性赋值
        field.set(car,"奔驰");
        System.out.println(field.get(car));
//        得到所有的属性
        Field[] fields = cls.getFields();
        System.out.println("====所有字段属性====");
        for (Field field1 :fields) {
            System.out.println(field1.getName());

        }


    }
}
/*
class Reflection_.Car
class java.lang.Class
Reflection_
Reflection_.Car
Car{name='宝马', price=10000, color='蓝色'}
宝马
奔驰
====所有字段属性====
name
price
color

获取Class对象六种方式

  1. 编译阶段:Class.forName()

  2. Class类加载阶段:类.class

  3. 运行阶段:对象.getClass()

  4. 类加载器:

  5. Class.forName():

​ 前提:已知一个类的全类名,且该类在类路径下,可通过Class类的静态方法forName()获取,可能抛出ClassNotFoundException,实例:Class.forName(Reflection_.Cat)

​ 应用场景:多用于配置文件,读取类全路径,加载类

  1. 类名.class:

​ 前提:已知具体的类,通过类的class获取,该方法最为安全可靠,程序性能最高,实例:Class cls2 = Cat.class

​ 应用场景:多用于参数传递

  1. 对象.getClass():

​ 前提:已知某个对象的实例,调用该实例的getClass()方法获取,实例:Class cls = 对象.getClass()

​ 应用场景:通过创建好的对象,获取Class对象

  1. 类加载器:

​ ClassLoader classLoader = cls3.getClass().getClassLoader();

​ Class<?> cls4 = classLoader.loadClass(“Reflection_.Car”);

  1. 基本数据类型:

​ Class cls = 基本数据类型.class

  1. 基本数据类型对应包装类:

    ​ Class cls = 包装类.TYPE

package Class_;

import Reflection_.Car;

public class GetClass_ {
    public static void main(String[] args) throws ClassNotFoundException {
//        1
        Class cls = Class.forName("Reflection_.Car");
        System.out.println(cls);

//        2
        Class cls2 = Car.class;
        System.out.println(cls2);

//        3
        Car cls3 = new Car();
        System.out.println(cls3.getClass());

//        4通过类加载器
//        (1)获取类加载器
        ClassLoader classLoader = cls3.getClass().getClassLoader();
//        (2)通过类加载器得到Class对象
        Class<?> cls4 = classLoader.loadClass("Reflection_.Car");
        System.out.println(cls4);

//        5基本数据类型
        Class<Integer> integerClass = int.class;
        System.out.println(integerClass);

//        6.基本数据类型的包装类型
        Class<Integer> type = Integer.TYPE;
        System.out.println(type);
        Class<Character> type1 = Character.TYPE;
        System.out.println(type1);

//      判断相等否,java底层自动装箱拆箱,是相等的
        System.out.println(integerClass.hashCode());
        System.out.println(type.hashCode());
    }
}
/*
class Reflection_.Car
class Reflection_.Car
class Reflection_.Car
class Reflection_.Car
int
int
char
589431969
589431969

那些类有Class对象

  1. 外部类,成员内部类,静态内部类,局部内部类,匿名内部类
  2. interface接口
  3. 数组
  4. enum枚举
  5. annotation注解
  6. 基本数据类型
  7. void
package Class_;

import java.io.Serializable;

public class AllTypeClass {
    public static void main(String[] args) {
        Class<String> c1 = String.class;
        Class<Serializable> c2 = Serializable.class;
        Class<Integer[]> c3 = Integer[].class;
        Class<float[][]> c4 = float[][].class;
        Class<Deprecated> c5 = Deprecated.class;
        Class<Thread.State> c6 = Thread.State.class;
        Class<Long> c7 = long.class;
        Class<Void> c8 = void.class;
        Class<Class> c9 = Class.class;

        System.out.println(c1);
        System.out.println(c2);
        System.out.println(c3);
        System.out.println(c4);
        System.out.println(c5);
        System.out.println(c6);
        System.out.println(c7);
        System.out.println(c8);
        System.out.println(c9);
    }
}
/*
class java.lang.String
interface java.io.Serializable
class [Ljava.lang.Integer;
class [[F
interface java.lang.Deprecated
class java.lang.Thread$State
long
void
class java.lang.Class

3.类加载

静态加载和动态加载

静态加载:编译时加载相关的类,如果没有则报错,依赖性太强

动态加载:运行时加载相关的类,如果运行时不用该类,则不会报错,降低了依赖性

类加载时机:

  1. 创建对象时 //静态加载
  2. 子类被加载时 //静态加载
  3. 调用类中的静态方法 //静态加载
  4. 通过反射 //动态加载

类加载过程

在这里插入图片描述在这里插入图片描述

加载:JVM在该阶段主要目的时将字节码从不同的数据源(可能是class文件、也可能是jar包,网络)转化为二进制字节流加载到内存中(方法区)并生成应该代表该类的java.lang.Class对象

验证:目的是确保Class文件的字节流中包含的信息符合当前虚拟机的要求,并且不会危害虚拟机自身的安全

​ 包括:文件格式验证(是否以魔数oxcafebabe开头),元数据验证,字节码验证和符号引用验证

​ 可以考虑使用 -Xverify:none参数来关闭大部分的类验证措施,缩短虚拟机类加载的时间

准备:JVM对静态变量进行默认初始化并分配空间(对应默认初始值,如0,0L,null,false等)这些变量所使用的内存都将在方法区进行分配

class A{
    //a1是实例属性,不是静态变量,因此在准备阶段不会分配内存
    //a2是静态变量,分配内存a2 是默认初始化0, 而不是20
    //a3是static final 是常量,他和静态变量不一样,会一次性分配,因为一旦赋值就不变,a3直接为30
    public int a = 10;
    public static int a1 = 20;
    public static final int a3 = 30;
}

解析:JVM将常量内的符号引用()转为直接引用(内存地址)

初始化:真正开始执行类中定义的Java程序代码,此阶段是执行< clinit >()方法的过程

​ < clinit>()方法是由编译器按语句在源文件中出现的顺序,依次自动收集类中所有静态变量的复制动作和静态代码块中的语句,并进行合并

​ 虚拟机会保证一个类的 clinit()方法在多线程环境中被正确的枷锁,同步,如果多个线程同时去初始化一个类,那么只有一个线程去执行类的clinit()方法,其他线程都需阻塞等待,知道活动线程执行完毕

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

//        1.加载A类,并生成A的class对象
//        2.链接 a=0
//        3.初始化阶段
//        依次收集静态变量的复制动作和静态代码块中的语句,并合并
/*          clinit(){
                静态代码块
                a = 300;
                a = 3;
            }
            合并 a =3;
            A构造器
 */
        A a = new A();
        System.out.println(A.a);
    }
}


class A{
    static {
        System.out.println("静态代码块");
        a = 300;
    }
    public  static int a = 3;

    A(){
        System.out.println("A的构造器");
    }
}

/*
静态代码块
A的构造器
3

4.获取类的相关信息

Class常用API

1.getName:获取全类名
2.getSimpleName:获取简单类名
3.getFields:获取所有public修饰的属性,包含本类以及父类的
4.getDeclaredFields:获取本类中所有属性
5.getMethods:获取所有public修饰的方法,包含本类以及父类的
6.getDeclaredMethods:获取本类中所有方法
7.getConstructors::获取本类所有publicf修饰的构造器
8.getDeclaredConstructors:获取本类中所有构造器
9.getPackage:以Package形式返回包信息
10.getSuperClass:以Classj形式返回父类信息
11.getInterfaces:l以Class[]形式返回接回信息
12.getAnnotations:以Annotation[]形式返回注解信息

package Reflection_;

import org.junit.jupiter.api.Test;

import java.lang.annotation.Annotation;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;

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

    }


    @Test
    public void test1() throws ClassNotFoundException {

        Class cls = Class.forName("Reflection_.Person");
//        1.getName:获取全类名
        System.out.println(cls.getName());

//        2.getSimpleName:获取简单类名
        System.out.println(cls.getSimpleName());

//        3.getFields:获取所有oublic修饰的属性,包含本类以及父类的
        Field[] fields = cls.getFields();
        for (Field field : fields) {
            System.out.println("父类及本类的所有public属性"+field.getName());
        }

//        4.getDeclaredFields:获取本类中所有属性
        Field[] fields1 = cls.getDeclaredFields();
        for (Field field : fields1) {
            System.out.println("父类及本类的所有属性"+field.getName());
        }

//        5.getMethods:获取所有public修饰的方法,包含本类以及父类的
        Method[] methods = cls.getMethods();
        for (Method method : methods) {
            System.out.println("本类及父类的所有public方法"+method.getName());
        }

//        6.getDeclaredMethods:获取本类中所有方法
        Method[] methods1 = cls.getDeclaredMethods();
        for (Method method : methods1) {
            System.out.println("本类及父类的所有方法"+method.getName());
        }

//        7.getConstructors::获取本类所有publicf修饰的构造器
        Constructor[] constructors = cls.getConstructors();
        for (Constructor constructor : constructors) {
            System.out.println("本类的public构造器"+constructor.getName());
        }

//        8.getDeclaredConstructors:获取本类中所有构造器


//        9.getPackage:以Package形式返回包信息
        System.out.println(cls.getPackage());

//        10.getSuperClass:以Class形式返回父类信息
        Class superClass = cls.getSuperclass();
        System.out.println(superClass);

//        11.getInterfaces:l以Class[]形式返回接回信息
        Class[] interfaces = cls.getInterfaces();
        for (Class anInterface : interfaces) {
            System.out.println("接口信息"+anInterface);
        }

//        12.getAnnotations:以Annotation[]形式返回注解信息
        Annotation annotations[] = cls.getAnnotations();
        for (Annotation annotation : annotations) {
            System.out.println("注解信息"+annotation);
        }
    }
}


class A{
    public String hobby;

    public A(){}

    public void hi(){

    }
}

interface  IA{

}
interface  IB{

}
@Deprecated
class Person extends  A implements IA,IB{
    private String name;
    protected  int age;
    String job;
    public  int salary;

    public Person(){

    }
    public void m1(){

    }

    protected  void m2(){

    }

    void m3(){

    }
    public void m4(){

    }
}

Field类常用API

  1. getModifiers()以int形式返回修饰符,默认是0,public 是 1, private 是 2,protected 是 4, static 是 8,final 是 16,public final是 public(1)+final(16)
  2. getType得到该属性的类型,以Class形式获取,返回数组
  3. getName返回属性名

Method类常用API

  1. getModifiers()以int形式返回修饰符,默认是0,public 是 1, private 是 2,protected 是 4, static 是 8,final 是 16,public final是 public(1)+final(16)
  2. getReturnType得到该属性的类型,以Class形式获取,返回数组
  3. getName返回属性名
  4. getParameterTypes,以Class[]返回参数类型数组

Constructor类

  1. getModifiers()以int形式返回修饰符,默认是0,public 是 1, private 是 2,protected 是 4, static 是 8,final 是 16,public final是 public(1)+final(16)
  2. getType得到该属性的类型,以Class形式获取,返回数组
  3. getParameterTypes 以Class类型返回参数类型数组
package Reflection_;

import org.junit.jupiter.api.Test;

import java.lang.annotation.Annotation;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.time.Period;

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

    }
    @Test
    public void test2() throws ClassNotFoundException{
        Class cls = Class.forName("Reflection_.Person");
//        4.getDeclaredFields:获取本类中所有属性
        Field[] fields1 = cls.getDeclaredFields();
        for (Field field : fields1) {
            System.out.println("父类及本类的所有属性\t"+field.getName()+"属性修饰符\t"
                    +field.getModifiers()+"属性类型\t"+field.getType());
        }

        Method[] methods1 = cls.getDeclaredMethods();
        for (Method method : methods1) {
            System.out.println("============");
            System.out.println("本类及父类的所有方法\t"+method.getName()+"属性修饰符\t"
            +method.getModifiers()+"属性类型\t"+method.getReturnType()
            +"");
            Class[] parameterTypes =  method.getParameterTypes();
            for (Class parameterType : parameterTypes) {
                System.out.println("该方法的形参"+parameterType);
            }
        }

        Constructor[] constructors = cls.getDeclaredConstructors();
        for (Constructor constructor : constructors) {
            System.out.println("============");
            System.out.println("本类的构造器"+constructor.getName());
            Class[] parameterTypes = constructor.getParameterTypes();
            for (Class parameterType : parameterTypes) {
                System.out.println("构造器形参"+parameterType);
            }
        }


    }
class A{
    public String hobby;

    public A(){}

    public void hi(){

    }
}

interface  IA{

}
interface  IB{

}
@Deprecated
class Person extends  A implements IA,IB{
    private String name;
    protected  int age;
    String job;
    public  int salary;

    private Person(double b){

    }
    public Person(String name, int age){

    }
    public void m1(String name, int age, double salary){

    }

    protected  String  m2(){
        return null;
    }

    void m3(){

    }
    public void m4(){

    }
}
/*
父类及本类的所有属性	name属性修饰符	2属性类型	class java.lang.String
父类及本类的所有属性	age属性修饰符	4属性类型	int
父类及本类的所有属性	job属性修饰符	0属性类型	class java.lang.String
父类及本类的所有属性	salary属性修饰符	1属性类型	int
============
本类及父类的所有方法	m2属性修饰符	4属性类型	class java.lang.String
============
本类及父类的所有方法	m1属性修饰符	1属性类型	void
该方法的形参class java.lang.String
该方法的形参int
该方法的形参double
============
本类及父类的所有方法	m3属性修饰符	0属性类型	void
============
本类及父类的所有方法	m4属性修饰符	1属性类型	void
============
本类的构造器Reflection_.Person
构造器形参double
============
本类的构造器Reflection_.Person
构造器形参class java.lang.String
构造器形参int

进程已结束,退出代码0

5.反射暴破

反射暴破创建实例

Class类相关方法

new Instance:调用public无参构造器,获取对象

getConstructor() 根据参数列表,获取对应的public构造器

getDecalaredConstructor() 根据参数列表获取对应构造器

Constructor类相关方法

setAccessible:暴破,使反射可以调用私有方法

newInstancce()调用构造器

例:

package Reflection_;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;

public class CreateClass {
    public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException {
//        获取到User类的对象
        Class cls = Class.forName("Reflection_.User");
//        无参
        Object o = cls.newInstance();
        System.out.println(o);

//        public有参
//        此时constructor就是
//        public User(String name) {//public的有参构造器
//           this.name = name;
//        }
        Constructor constructor = cls.getConstructor(String.class);
        Object m = constructor.newInstance("美羊羊");
        System.out.println(m);

//        非public有参
        Constructor constructor1 = cls.getDeclaredConstructor(int.class, String.class);
//       添加暴破,使用反射可以使用private构造器
        constructor1.setAccessible(true);
        Object n = constructor1.newInstance(18, "暖羊羊");
        System.out.println(n);


    }
}

class User {
    private int age = 10;
    private String name = "喜羊羊";

    public User() {//无参public
    }

    public User(String name) {//public的有参构造器
        this.name = name;
    }

    private User(int age, String name) {//private有参构造器
        this.age = age;
        this.name = name;
    }

    public String toString() {
        return "User [age=" + age + "name=" + name + "]";
    }

}
/*
User [age=10name=喜羊羊]
User [age=10name=美羊羊]
User [age=18name=暖羊羊]

操作类中属性

Field f = clazz对象.getDeclaredField(属性名)

暴破:f.setAccessible(true)

访问:f.set(o, 值) f.get(o) o表示对象 若是静态属性,则o可以写成null

package Reflection_;

import java.lang.reflect.Field;

public class Accessproperty {
    public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchFieldException {
        Class cls = Class.forName("Reflection_.Student");
        Object o = cls.newInstance();//o的运行类型已经是Student
//        使用反射操作age
        Field age = cls.getField("age");
        age.set(o, 18);//通过反射操作属性
        System.out.println(o);
        System.out.println(age.get(o));

//        使用反射操作name
        Field name= cls.getDeclaredField("name");
        name.setAccessible(true);
//        name.set(o,"暖羊羊");
        name.set(null,"暖羊羊");
        System.out.println(o);

        System.out.println(name.get(null));

    }
}

class Student{
    public   int age;
    private  static  String  name;

    public Student(){

    }

    @Override
    public String toString() {
        return "Student{" +
                "age=" + age +"name="+name+
                '}';
    }
}
/*
Student{age=18name=null}
18
Student{age=18name=暖羊羊}
暖羊羊

操作类中方法

暴破访问:method.setAccessble(true) 暴破方法

访问:Object returnValue = method.invoke(o, 实参列表)//o就是对象

有返回值,统一返回Object类型,运行类型是method方法类型

如果是静态方法,o可以是null

package Reflection_;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class AccessMethod {
    public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException {
        Class cls = Class.forName("Reflection_.Boss");
        Object o = cls.newInstance();
        Method hi = cls.getMethod("hi", String.class);
        hi.invoke(o, "喜羊羊");

        Method say = cls.getDeclaredMethod("say",int.class,String.class,char.class);
        say.setAccessible(true);
        System.out.println(say.invoke(o,17,"沸羊羊",'s'));
//        因为是static方法,故o可以是null
        System.out.println(say.invoke(null,200,"红太狼",'l'));
    }
}

class Boss{
    public   int age;
    private  static  String  name;

    public Boss(){

    }

    private  static  String say(int age, String name,char c ){
        return name+"  "+age+"   "+c;
    }

    public   void hi(String s){
        System.out.println("hi"+s);
    }
}
/*
hi喜羊羊
沸羊羊  17   s
红太狼  200   l

  • 1
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值