美文网首页
Java语法 1小时入门

Java语法 1小时入门

作者: 彡廿 | 来源:发表于2019-10-22 18:50 被阅读0次

    1、基本数据类型

    1.1 Java是强类型语言

    • 所有变量必须先声明后使用。
    • 指定类型的变量只能接受与它类型匹配的值。

    1.2 Java的类型分类

    • 基本类型
    • 引用类型
    public class Main {
    
        public static void main(String[] args) {
            //  基本数据类型
            byte a_byte = 1;
            short a_short = 1;
            int a_int = 1;
            long a_long = 1;
            float a_float = 1.0f;
            double a_double = 1.0;
            boolean a_boolean = true;
            char a_char = 'c';
        }
    }
    

    1.3 类型转换

    1.3.1 自动类型转换

    范围小可以自动转化为范围大的数据类型。

    1.3.2 强制类型转换

    范围大的向范围小转换。

    语法:

    (targetType)value
    

    1.3.3 表达式类型的自动提升

    • 所有的byte short char 提升到int
    • 数据类型自动提升为最高等级操作数的类型

    1.4 直接量

    能指定直接量的三种类型: 基本类型,字符串类型,null类型。

    tips: 如果程序第一次使用字符串直接量,java会使用常量池(constant pool)来缓存改字符串直接量。后面在使用该字符串直接量,会直接使用常量池中的字符串直接量。

    1.5 运算符

    1.5.1 算术运算符

    + - * / += -= *= /=
    

    2、流程控制

    public static void main(String[] args) {
        // if else
        int score = 50;
        if (score > 90) {
            System.out.println("优秀");
        } else if (score > 70) {
            System.out.println("中等");
        } else if (score > 60) {
            System.out.println("及格");
        } else {
            System.out.println("差的");
        }
    
        //  只能是 byte short char int enum String 不能是boolean
        //  可以省略 case 后面的花括号
        switch (score) {
            case 50:
                System.out.println("50分");
                System.out.println("不够优秀");
                break;
            default:
                System.out.println("考了 = " + score);
                System.out.println("优秀吗");
                break;
        }
    
        // while
        int count = 10;
        while (count < 20) {
            System.out.println("---------" + count);
            count++;
        }
    
        // do while
        do {
            System.out.println("----------" + count);
            count--;
        } while (count < 10);
    
        // for
        for (int i = 0; i < 10; i++) {
            System.out.println("i = " + i);
        }
    }
    

    3、 面向对象

    3.1) 基本语法

    
    public class Person {
        // filed
        private static String country;
        private int age = 0;
        private float height;
        private boolean sex;
    
        // 非静态变量的初始化代码块
        {
            System.out.println("非静态初始化代码块");
            age = 10;
            height = 12.9f;
            sex = true;
        }
    
        // 静态变量的初始化块
        static {
            System.out.println("静态初始化代码块");
            country = "china";
        }
    
        // 构造方法,如果不定义构造方法,系统自动会生成一个默认的没有参数的构造方法
        public Person(int age, float height, boolean sex) {
            this.age = age;
            this.height = height;
            this.sex = sex;
        }
    
        // 静态方法
        public static void say() {
            System.out.println("I'm " + country);
        }
    
        // 可变参数,values相当于一个数组
        public void eat(Object... values) {
            for (Object value : values) {
                System.out.println(value);
            }
        }
    
        // 方法的重装,方法名相同,参数列表不同,返回值类型,和修饰符和方法重载没有关系
        public void work(String job) {
            System.out.println("job is" + job);
        }
    
        public void work(String job, String where) {
            System.out.println("at " + where + "job is" + job);
        }
    
        @Override
        public String toString() {
            return ("age =" + age + "height =" + height + "sex =" + sex);
        }
    
        public static void main(String[] args) {
            Person person = new Person(10, 121.0f, false);
            System.out.println(person);
        }
    }
    
    

    局部变量定以后,必须显示初始化才能使用。

    成员变量定义后,不必初始化,系统会给一个默认值。

    3.1.1)访问权限修饰符:

    public > protected > default > private
    

    一个类就是一个小模块,在程序设计时,应尽量避免一个模块直接操作和访问另一个模块的数据,模块设计要求高内聚,低耦合。

    // 声明包
    package 包名
    
    // 导包
    import 包
    // 静态导入,导入静态变量,静态方法
    import static 包
    

    java中常用的包

    java.lang
    java.util
    java.net
    java.io
    java.text
    java.sql
    

    3.1.2)继承

    java的类只能有一个直接父类。

    子类可以复写父类的方法,遵循一个原则:两同两小一大。

    两同:方法名相同,形参列表相同。
    两小:子类方法返回值类型应比父类方法返回值类型更小或者相等,子类方法声明抛出的异常比父类方法声明抛出的异常更小或者相等。
    一大:子类方法的访问权限应比父类方法的访问权限更大或者相等。
    

    3.1.3 ) super

    
    public class SubClass extends SuperClass {
        private int a;
    
        public SubClass(int a) {
            // 调用父类被复写的方法
            super(a);
        }
    
        @Override
        public void test() {
            // 调用父类被隐藏的变量
            System.out.println(super.a);
        }
    }
    
    class SuperClass {
        public int a;
    
        public SuperClass(int a) {
            this.a = a;
        }
    
        public void test() {
            System.out.println(a);
        }
    }
    

    3.2) 多态

    引用变量有两种类型:一个编译时类型,一个是运行时类型。

    
    class Person {
        private String name;
    
        public Person(String name) {
            this.name = name;
        }
    
        public void say() {
            System.out.println("I'm " + name);
        }
    }
    
    public class Man extends Person {
        private int age;
    
        public Man(String name, int age) {
            super(name);
            this.age = age;
        }
    
        @Override
        public void say() {
            super.say();
            System.out.println("age is " + age);
        }
    
        public static void main(String[] args) {
            // 编译时 Person 运行时 Man
            Person person = new Man("wangbo", 20);
            person.say();
        }
    }
    

    3.2.1)引用变量的强制类型转换

    • 基本类型之间的转换只能在数值类型之间进行。
    • 引用类型之间的转换只能在具有继承关系的两个类型之间进行。

    3.2.2)instanceof 运算符

    注意:

    • instanceof是运算符不是一个方法。
    • 它的作用是判断前面的对象是否是后面的类或者子类的实例。如果是返回true,否则返回false。
    • 前面的操作数编译时类型要么与后面的类型相同,要么与后面的类型具有父子继承关系,否则编译错误。
    null instancof Person //返回永远为false,null不是
    if (null instanceof Person){
        System.out.println(true);
    } else {
        System.out.println(false);
    }
    

    继承破坏封装,建议采用组合实现复用

    • 继承是 is-a关系
    • 组合是 has-a关系

    3.3 初始化块

    它是类的第四个成员(成员变量,方法,构造器)。初始化块和构造器的作用类似,但比构造器率先执行。

    static {
      // 静态初始化块,对类进行操作,同样不能访问非静态成员
    }
    
    {
      // 普通初始化块,对对象进行操作
    }
    

    执行顺序:

    静态初始化块 --> 前面执行(普通初始化块,声明实例变量指定的默认值) --> 构造器
    

    普通初始化块和声明实例变量指定的默认值,谁在前面谁先执行。

    3.3.1) 初始化块与构造器

    • 初始化块是一段固定执行的代码,不能接受任何参数。因此初始化块对同一个类的所有对象所执行初始化处理完全相同。
    • 构造器接受参数,不同对象所执行的初始化处理不同。
    • 初始化块其实是个假象,编译后初始化块会还原到每个构造器中,且位于构造器所有代码掐面。

    java文件

    public class InstanceInit {
      // 初始化块和声明默认初始化,谁在后面谁的值放到构造器中
        {
            a = 6;
        }
        private int a = 9;
    
        public static void main(String[] args) {
            System.out.println(new InstanceInit().a);
        }
    }
    

    class文件

    public class InstanceInit {
        private int a = 6;
    
        public InstanceInit() {
            this.a = 9;
        }
    
        public static void main(String[] args) {
            System.out.println((new InstanceInit()).a);
        }
    }
    

    3.4 包装类

    基本数据类型对应的类。

    byte -> Byte
    short -> Short
    int -> Integer
    boolean -> Boolean
    long -> Long
    char -> Character
    float -> Float
    double -> Double
    

    JDK1.5之前,装箱和拆箱比较麻烦,JDK1.5之后能够自动装箱和拆箱

    3.4.1) 自动装箱和拆箱

    // 自动装箱
    Integer inObj = 8;
    Object boolObj = true;
    
    // 自动拆箱
    int it = inObj;
    

    3.4.2)String与基本类型之间的转换

    // 字符串转基本类型
    String intStr = "123";
    int it1 = Integer.parseInt(intStr);
    int it2 = new Integer(intStr);
    
    // 基本类型转换字符串
    String ftStr = String.valueOf(2.35f);
    

    3.5 ) toString、== 、equals方法

    toString方法在直接打印对象时,自动调用的方法。一般会自定义,来输出想输出的属性

    ==

    • 基本类型:直接判断值是否相等

    • 引用类型:判断引用是否是同一个对象,不能用于比较类型上没有父子关系的两个对象。

      [图片上传失败...(image-e4fa44-1571732324683)]

    equals()

    • equals()方法是Object类提供的一个实例方法。
    • 如果不复写的话,和 == 一样也是用来判断是否指向同一个对象。
    • 一般复写,提供自定义的相等标准。
    public class Person {
        private String name;
        private String idStr;
    
        public Person(String name, String idStr) {
            this.name = name;
            this.idStr = idStr;
        }
    
        @Override
        public boolean equals(Object obj) {
            if (this == obj) {
                return true;
            }
            // instanceof 运算符前面对象是后面类的实例活其子类的实例都返回true,所有用instanceof判断对象是否为同一个可能有问题
            if (obj != null && obj.getClass() == Person.class) {
                Person person = (Person) obj;
                if (this.idStr.equals(person.idStr)) {
                    return true;
                }
                return false;
            }
            return false;
        }
    }
    

    3.6)类成员

    java类里只能含有成员变量,方法,构造器,初始化块,内部类(接口,枚举)五种。

    3.7)final修饰符

    final关键字可用于修饰类、变量和方法。

    3.7.1)final成员变量

    final变量获得初始值之后不能被重新赋值。

    final修饰的成员变量必须由程序员指定初始值,系统不会指定默认值。

    • 类变量:

      • 静态初始化块
      • 声明类变量时指定默认值
    • 实例变量:

      • 非静态初始化块
      • 声明实例变量指定默认值
      • 构造函数中指定初始值

      只能是其中之一初始化

    分析原因:

    final修饰的成员变量,如果不指定初始值,系统分配默认值,之后不能修改。这些成员变量也就失去了意义,所以系统要求final成员变量必须主动初始化。
    

    3.7.2)final修饰局部变量

    局部变量系统不会提供默认值,final修饰的局部变量也可以不指定默认值,后面再指定。

    public class Person {
        
        public void test(final int a){
            // 不能对final修饰的形参赋值
            // a = 5;
        }
        
        public static void main(String[] args) {
            final String name;
            name = "hello";
            System.out.println(name);
        }
    }
    

    3.7.3)final修饰基本类型变量和引用类型变量的区别

    public class Person {
        public static void main(String[] args) {
            // 修饰基本类型,不能修改值
            final int age = 10;
            // age = 11;
            
            // 修饰引用类型
            final List<String> stringList = new ArrayList<>();
            
            // 引用类型的指针地址不变,内容改变
            for (int i = 0; i < 10; i++) {
                stringList.add("--" + i);
            }
        }
    }
    

    3.7.4) final 方法

    final修饰的方法不能被复写,但是可以重载。

    public class FinalMethodTest {
        public final void test(){}
    }
    
    class Sub extends FinalMethodTest{
        // 下面方法定义将编译出错,不能复写final方法
        public void test(){}
    }
    
    public class FinalMethodTest {
        private final void test(){}
    }
    
    class Sub extends FinalMethodTest{
        // 父类private的方法,子类不可见,子类中的test是重定义的
        private void test(){}
    }
    

    3.7.5)final类

    final类不能被继承。

    3.7.6)不可变类

    • private和final修饰成员变量
    • 提供参数构造器,用于传入参数初始化类的成员变量
    • 仅为该类的成员变量提供getter方法,不要为该类提供setter方法
    • 如有必要,重写hasCode()和equals()方法。
    • 线程安全的。

    一般不可变量用于数据层的itemInfo。不可变类的引用类型的成员变量,在赋值的时候最好复制一份,避免直接赋值,因为外部可能修改这个引用类型的变量。

    public class ImmutableClass {
        private final int id;
        private final String name;
    
        public ImmutableClass(int id, String name) {
            this.id = id;
            this.name = name;
        }
    
        public int getId() {
            return id;
        }
    
        public String getName() {
            return name;
        }
    
        @Override
        public boolean equals(Object o) {
            if (this == o) return true;
            if (o == null || getClass() != o.getClass()) return false;
            ImmutableClass that = (ImmutableClass) o;
            return id == that.id &&
                    Objects.equals(name, that.name);
        }
    
        @Override
        public int hashCode() {
            return Objects.hash(id, name);
        }
    }
    

    3.8)抽象类

    抽象类是从多个类中抽象出来的模板。

    • 抽象类被 abstract 修饰,抽象方法也被abstract修饰,不能有方法体。
    • 抽象类不能实例化,可以包含成员变量,方法(普通方法和抽象方法)构造器,初始化块,内部类(接口枚举)5种成分。
    • 包含抽象方法的类,只能定义成抽象类。
    • final 和 abstract 不能共存
    • abstract 和 static不能同时修饰某个方法,但却可以修饰内部类。
    • abstract 和 private 不能共存。

    抽象类的作用: 提供模板,避免子类设计的随意性。

    3.8.1)定义

    public class Man extends Person {
        @Override
        void eat(String food) {
            System.out.println("eat");
        }
    
        @Override
        void work() {
            System.out.println("work");
        }
    }
    
    abstract class Person {
        abstract void eat(String food);
    
        abstract void work();
    
        void sleep() {
            System.out.println("睡觉");
        }
    }
    

    3.9)接口

    类是具体的实现,接口定义时多个类共同的公共行为规范,是与外部交流的通道。

    软件设计:通常会采用面向结构的设计,来实现模块之间的低耦合,同时可扩展性和可维护性更好。

    接口的特性:

    • 接口可以多继承,类只能单继承。
    • Java 8以上接口允许定义默认方法,类方法。
    • 接口是一种规范,不能包含构造器和初始化块,可以包含承欢变量(只能是静态常量)方法(只能是抽象方法,类方法,默认方法)内部类(包含内部接口,枚举)定义。
    • 接口是公共行为的规范,所以所有的成员都是public权限。
    • 接口定义的内部类,内部接口,内部枚举都默认采用public static修饰。
    • 接口是一种更加抽象的类,所以java源文件里只能有一个public接口且必须与文件重名。

    3.9.1)定义

    public interface Output {
        // int MAX_CACHE_LINE = 50; 系统会自动为接口定义的成员变量增加public static修饰符
        public static final int MAX_CACHE_LINE = 50;
    
        // 接口定义的普通方法默认是public抽象方法
        void out();
    
        void getData(String msg);
    
        // 接口里的默认方法,需要使用default修饰
        default void print(String... msgs) {
            for (String msg : msgs) {
                System.out.println(msg);
            }
        }
    
        // 接口里的类方法,需要static修饰
        static String staticTest() {
            return "接口里的方法";
        }
    }
    

    3.9.2)接口和抽象类的区别

    • 接口是模块与外界通信桥梁,体现的是一种规范。
    • 抽象类是一种模板式设计,多个子类的共同父类。

    3.10)内部类

    • 内部类提供了更好的封装,不允许同一个包下的其他类访问
    • 内部类可以直接访问外部的成员,反过来却不行
    • 匿名内部类适用于仅需创建一次使用的类。
    • 内部类比外部类可以多使用三个修饰符:private protected static,外部类不可以使用
    • 非静态内部类,不能拥有静态成员。
    • 非静态内部类实例必须寄生在外部类的实例中,如果其他地方需要使用,就没必要设计成内部类了。

    成员内部类,局部内部类,匿名内部类,静态内部类,非静态内部类。

    静态内部类

    • 静态内部类不能访问外部类的非静态成员。
    • 可以包含静态成员。
    • 只需要把外部类当成静态内部类的包空间,静态内部类和外部类没啥差别。
    • 优先使用静态内部类

    匿名内部类:

    • 一般是通过接口定义的形式实现匿名内部类
    • 匿名内部类只有一个默认的无参构造方法
    • java 8之前,要求被局部内部类,匿名内部类访问的局部变量必须是final修饰,java8之后取消了限制,符合上述规则,自动添加final修饰符。

    3.11)Lambda表达式

    • 用于简化匿名内部类的创建。

    • 函数式接口代表只包含一个抽象方法的接口。函数式接口可以包含多个默认方法,类方法,但只能声明一个抽象方法。

    • Java8 为函数式接口提供了@FuncaitonInterface注解。

    • lambda表达式可以用来赋值。

    3.12)引用方法

    className::类方法
    object::实例方法
    className::new
    

    3.13)枚举类

    • 枚举类可以实现一个或多个接口,enum定义的枚举类默认继承java.lang.Enum类,Enum实现了Serializable和Comparable接口。
    • enum定义非抽象的枚举类,默认使用final修饰。因此枚举类不能派生子类
    • 枚举类的构造器只能使用private修饰,省略默认是使用private。
    • 枚举类的所有势力必须在枚举类的第一行显示列出,否则这个枚举类永远不能产生实例,这些实例系统会自动加上public static final修饰。
    • 枚举类默认提供了一个value()方法,该方法可以方面遍历所有的枚举值。
    • 枚举类的实例只能是枚举值,而不是随意通过new来创建枚举类对象。
    public enum SearchType {
      // 此处的枚举值必须调用对应的构造函数来创造
        All("all"),
        Product("product"),
        Article("review");
    
      // 枚举成员一般不可变,这样更安全
        private final String mId;
    
        SearchType(String id) {
            mId = (id == null ? "" : id.trim().toLowerCase(Locale.US));
        }
    
        public String getId() {
            return mId;
        }
    
        public static SearchType getById(String id) {
            id = (id == null ? "" : id.trim().toLowerCase(Locale.US));
            for (SearchType type : SearchType.values()) {
                if (id.equals(type.mId)) {
                    return type;
                }
            }
    
            return null;
        }
    }
    

    3.14)对象与垃圾回收

    • 垃圾回收机制只负责回收堆内存中对象,不会回收任何物力资源
    • 程序员无法精确控制垃圾回收的运行,垃圾回收会在合适的时候运行。当对象永久性地失去引用后,系统就会在合适的时候回收它所占内存。
    • 在垃圾回收机制回收任何对象之前,总会先调用它的finalize()方法,该方法可能使该对象重新复活(让一个引用变量重新引用对象),从而导致垃圾回收机制取消回收。

    3.14.1)对象在内存中状态

    • 可达状态:当一个对象有一个以上的引用变量引用。
    • 可恢复状态:某个对象不再有任何引用变量引用它,它就进入可恢复状态。在这状态下,系统的垃圾回收机制准备回收该对象占用的内存,在回收之前会调用finalize()方法进行资源清理,在finalize()调用时可能重新被引用,变成了可达状态。如果确实没有被引用在进入不可达状态。
    • 不可待状态:当对象与所有引用变量的关联都被切断,且系统已经调用了finalize()方法后依然没有使该对象编程可达状态,那么这个对象将永久性地失去应用,变成不可达状态。只有不可达状态,系统才会真正回收该对象占用资源。

    [图片上传失败...(image-25ef57-1571732324683)]

    3.14.2)强制垃圾回收

    程序无法精确控制java垃圾回收机制,但是可以强制系统进行垃圾回收 ——这种强制只是通知系统进行垃圾回收,但系统是否进行垃圾回收依然不确定。

    • 调用System 类的gc()静态方法:System.gc()
    • 调用Runtime 对象的 gc()实例方法:Runtime.getRuntime().gc()

    finalize()方法

    • 永远不用主动调用某个对象的finalize()方法,该方法应交给垃圾回收机制调用。
    • finalize()方法合适被调用,是否被调用具有不确定性,不要把finalize()方法当成一定会被执行的方法。
    • 当JVM执行科恢复对象的finilize()方法时,可能是该对象或者系统中其他对象重新变成可达状态。
    • 当JVM执行finalize()方法是出现异常时,垃圾回收机制不会报异常,程序继续执行。
    • finalze()方法不定义会被执行,因此清理某个类的资源,则不要放在finalize()方法中进行清理。
    
    public class FinalizeClass {
        private static FinalizeClass finalizeClass = null;
    
        public void info() {
            System.out.println("测试资源清理的finalize方法");
        }
    
        public static void main(String[] args) {
            new FinalizeClass();
    //        通知系统进行资源回收
            System.gc();
    //        强制垃圾回收机制调用可恢复对象的finalize()方法
    //        Runtime.getRuntime().runFinalization();
            System.runFinalization();
    
            finalizeClass.info();
        }
    
        @Override
        protected void finalize() throws Throwable {
            super.finalize();
            // 让对象从可恢复状态变成了可达状态,内存泄漏
            finalizeClass = this;
        }
    }
    

    3.15)对象的强、软、弱、虚引用

    • 强引用(StrongReference)最常见的方式。
    • 软引用:通过SoftReference类来实现,当一个对象只有软引用是,他有可能被垃圾回收机制回收。垃圾机制运行时,当系统内存空间充足时,不会被系统回收。程序可使用该对象。当系统空间不足时,系统可能回收它,软引用用于内存敏感的程序中。用的比较少。
    • 弱引用:通过WeakReference类实现,弱引用和软引用很像。但若引用的引用级别更低。只有弱引用的对象,当垃圾回收机制运行时,不管内存是否充足,他都会被回收。
    • 虚引用:PhantomReference类实现。完全类似没有引用。主要用于跟踪对象被垃圾回收的状态。

    软、弱、虚都有一个get()方法,用于获取他们所引用的对象。

    4、泛型

    4.1) 定义泛型类和接口

    public interface Map<K, V> {
        Set<K> keySet();
    
        V put(K key, V value);
    }
    
    // 设定泛型上限
    public class Apple<T extends Number> {
        private T info;
    
        public Apple(T info) {
            this.info = info;
        }
    
        public Apple() {
        }
    
        public T getInfo() {
            return info;
        }
    
        public void setInfo(T info) {
            this.info = info;
        }
    
        public static void main(String[] args) {
            // 菱形语法
            Apple<String> apple = new Apple<>("苹果");
            Apple<Double> doubleApple = new Apple<>(13.0);
            
        }
    }
    

    4.2)泛型类派生子类

    // public class A extends Apple<T> 错误的做法
    // 派生类需要指定具体类型
    public class A extends Apple<String>
    
    public class A extends Apple
    

    注意

    List<A> 并不是 List<Apple>的子类
    
    List<Apple> list1 = new List<>();
    List<A> list2 = new List<>();
    List<String> list3 = new List<>();
    
    void test(List<Apple> list){
      // do something
    }
    
    test(list1); // 正确
    test(list2); // 错误
    test(list3); // 错误
    
    // ? 泛型通配符
    void print(List<?> list){
       // do something
    }
    
    print(list1); // 通过
    print(list2);  // 通过
    print(list3); // 通过
    
    // 限定 ? 泛型通配符
    void say(List<? extend Apple> list){
      // do something
    }
    
    say(list1); // 通过
    say(list2); // 通过
    say(list3); // 错误
    

    4.3)泛型方法

    修饰符 <T,S> 返回值类型<N,M> 方法名(形参列表){
    
    }
    
    public class TFuncation {
        static <T> void fromArrayToCollection(T[] a, Collection<T> c) {
            for (T o : a) {
                c.add(o);
            }
        }
    
        public static void main(String[] args) {
            Object[] oa = new Object[100];
            Collection<Object> co = new ArrayList<>();
            fromArrayToCollection(oa, co);
        }
    }
    

    5、基础类库

    5.1)系统相关

    System

    // 获取所有系统变量
    System.getenv();
    // 获取指定系统变量
    System.getenv("JAVA_HOME");
    // 获取所有系统属性
    System.getProperties();
    // 获取指定系统属性
    System.getProperty();
    // 通知系统进行垃圾回收的
    System.gc();
    // 通知系统进行资源清理
    System.runFinalization();
    // 获取系统当前的时间,时间粒度取决于操作系统,
    System.currentTimeMillis();  // 毫秒为单位,不准确,操作系统以几十毫秒为单位
    System.nanoTime();  // 纳秒为单位,很少用,操作系统不支持
    
    // 根据对象地址计算得到的hashCode值
    System.identityHashCode(Object x);
    

    Runtime

    // 通知系统进行垃圾回收
    Runtime.gc();
    // 清理系统资源
    Runtime.runFinalization();
    // 加载文件
    Runtime.load(String fileName);
    // 加载动态链接库
    Runtime.loadLibrary(String libname);
    
    
    public class RuntimeTest {
        public static void main(String[] args) {
            // 获得运行时队形
            Runtime runtime = Runtime.getRuntime();
            //  处理器数量
            System.out.println(runtime.availableProcessors());
            // 空闲内存数
            System.out.println(runtime.freeMemory());
            // 总内存数
            System.out.println(runtime.totalMemory());
            // 最大内存数
            System.out.println(runtime.maxMemory());
        }
    }
    

    5.2)Clone

    
    public class UserTest {
        public static void main(String[] args) throws CloneNotSupportedException {
            User user = new User(2);
            User user1 = user.clone();
            System.out.println(user == user1);  // false
            System.out.println(user.address == user1.address);  // true
        }
    }
    
    class Address {
        String detail;
    
        public Address(String detail) {
            this.detail = detail;
        }
    }
    
    class User implements Cloneable {
        int age;
        Address address;
    
        public User(int age) {
            this.age = age;
            address = new Address("北京海淀");
        }
    
        public User clone() throws CloneNotSupportedException {
            return (User) super.clone();
        }
    }
    

    clone只是一种浅克隆,只是对引用变量进行复制,对引用变量所引用的对象没有复制。引用变量所指向的内存还是同一个实例。

    5.3)新增Objects工具类

    Java为工具类的命名习惯是添加一个字母s。Arrays,Collections。

    public class ObjectsTest {
        static ObjectsTest objectsTest;
    
        private String name;
    
        public ObjectsTest(String name) {
            // 用来对方法形参进行输入校验,并自定义空指针异常
            this.name = Objects.requireNonNull(name, "name 不能为null");
        }
    
        public static void main(String[] args) {
            System.out.println(Objects.hashCode(objectsTest));
            System.out.println(Objects.toString(objectsTest));
            System.out.println(Objects.requireNonNull(objectsTest, "参数不能为null"));
        }
    }
    
    public static <T> T requireNonNull(T obj, String message) {
            if (obj == null)
                throw new NullPointerException(message);
            return obj;
        }
    

    5.4)StringBuffer类

    • String是不可变类
    • StringBuffer 可变字符串,append(),insert(),reverse(),setCharAt(),setLength().toString()线程安全的。
    • StringBuilder 可变字符串,和StringBuffer基本类似,没有实现线程安全功能,性能高。优先使用StringBuilder。

    5.5)随机数

    // 当前时间为随机数种子
    Random random = new Random(System.currentTimeMillis());
    int val = random.nextInt(10);
    // 线程安全的随机数
    ThreadLocalRandom threadLocalRandom = ThreadLocalRandom.current();
    int val2 = threadLocalRandom.nextInt(10, 100);
    

    5.6)BigDecimal类

    • float、double两种基本浮点类型容易引起精度丢失。
    • BigDecimal提供静态函数来对float double string等类型进行转换。
    • float double不推荐使用构造
    • String可以使用构造函数
    BigDecimal f1 = new BigDecimal("0.05");
    BigDecimal f2 = BigDecimal.valueOf(0.01);
    // 不推荐使用
    BigDecimal f3 = new BigDecimal(0.05);
    System.out.println("0.05 + 0.01 = " + f1.add(f2));  // 0.06
    System.out.println("0.05 - 0.01 = " + f1.subtract(f2)); // 0.04
    System.out.println("0.05 * 0.01 = " + f1.multiply(f2)); // 0.0005
    System.out.println("0.05 / 0.01 = " + f1.divide(f2)); // 5
    

    5.7)Calendar

    5.8)正则表达式

     // 将字符串编译成pattern对象
    Pattern p = Pattern.compile("a*b");
    Matcher m = p.matcher("aaaabbbb");
    boolean b = m.matches();
    
    • Pattern不可变类,可供多个线程并发使用。

    • Mather类提供了常用的方法

      find();// 返回目标字符串中是否包含与pattern匹配的字符串
      group(); // 返回上一次与Pattern匹配的字符串
      start();// 返回上次与pattern匹配的字符串开始位置
      end();// 返回上次与pattern匹配的字符串的结束位置
      lookingAt(); // 返回目标字符串前面部分与pattern是否匹配
      matches(); // 返回目标字符串是否与pattern匹配
      reset();// 将现有的Matcher对象应用于新的字符序列。
      

    6、集合

    数组是不可变的,集合是可变的。

    6.1)集合的常见操作

    集合是实现了Collection的接口的,Collection是继承了interator接口的。

    Collection collection = new ArrayList();
    collection.add("hello");
    // 自动装箱
    collection.add(6);
    //删除指定元素
    collection.remove(6);
    
    Collection books = new HashSet();
    books.add("轻量级java EE 企业应用之战");
    books.add("疯狂Java讲义");
    
    // 删除集合
    collection.removeAll(books);
    
    // 清空
    collection.clear();
    // books集合里剩下Collection也包含的集合
    books.retainAll(collection);
    
    // lambda表达式遍历集合
    books.forEach(obj -> System.out.println(obj));
    

    6.2)Iterator遍历集合元素

     // 迭代器遍历集合,并删除,迭代只是把集合元素的值传递给了迭代变量,修改迭代变量的值对集合元素本身没任何影响
    Iterator iterator = books.iterator();
    while (iterator.hasNext()) {
        String book = (String) iterator.next();
        if (book.equals("疯狂Java讲义")) {
            iterator.remove();
            // books.remove(book)将引发ConcurrentModificationException异常
        }
        book = "测试字符串";
    }
    

    6.3)HashSet类

    • 不能保证排序
    • 不是同步的
    • 集合元素值可以为null
    • 根据hashCode决定存储位置

    HashSet集合判断两个元素相等的标准是两个对象通过equals()并且两个对象的hashCode()方法返回值也相等。

    6.3.1) LinkedHashSet类

    • LinkedHashSet集合也是根据hashCode值来决定元素的存储位置。

    • 它使用链表维护元素的次序,遍历输出有次序

    • 性能低于LinkedHashSet,迭代访问性能较好

    • LinkedHashSet依然是HashSet,不允许集合元素重复

    6.3.2)TreeSet类

    • 是SortedSet接口的实现类,TreeSet可以确保集合元素处于排序状态

    • first() last() lower(Object e) higher(Object e) subSet() headSet(Object toElement)

      6.3.3) EnumSet类

    • 专为枚举类设计的集合类

    • 也是有序的集合

    • 不允许加入null元素

    6.3.4)HashSet和TreeSet性能对比

    • HashSet性能总比TreeSet好
    • TreeSet需要额外的红黑树算法来维护集合元素的次序。需要排序的Set用TreeSet。

    6.4)List集合

    • ArrayList不是线程安全的,Vector是线程安全的性能低

    7、异常处理

    7.1)异常概念

    异常处理让程序具有极好的容错性和健壮性。发生异常系统自动生成一个Exception对象来通知程序。

    java的异常分为Checked异常和Runtime异常。Checked异常可以在编译阶段被发现。Runtime异常只能在运行期间得到处理。

    • try : 可能发生异常的代码块
    • catch:用于处理这种类型的异常代码块
    • finally:回收try里打开的资源,除非在try catch块里调用退出虚拟机的方法 System.exit(1),不然即使调用return finally块依然执行。避免在finally块里使用return throw等方法。
    • throws:方法签名使用
    • throw:抛出实际的异常
    try {
        // 异常由运行时环境抛出
        int a = 0 / 2;
    } catch (Exception e) {
        // 处理异常
    } finally {
        // 无论如何都会执行
    }
    

    try业务逻辑出现异常,系统自动生成异常对象,该异常对象被提交给Java运行时环境,这个过程为抛出(throw)异常。Java运行时环境受到异常,寻找能处理该异常的catch块,找到就交给catch处理,找不到运行时环境终止,Java程序退出。

    7.2)异常类

    • 异常 Exception:可以用try catch处理。
    • 错误 Error : 与虚拟机相关的问题,无法恢复,不能捕获,所以不能用try catch捕获Error、

    都继承Throwable。

    先捕获范围小的异常,再捕获异常范围大的。

    getMessage():异常详细描述
    printStackTrace(): 输出跟踪信息栈
    printStackTrace(PrintStream s): 输出跟踪信息栈到s流
    getStackTrace(): 获得跟踪栈信息
    

    8、注解

    注解是一种接口。都有一个 value属性。

    JDK5增加了元数据(MetaData)支持,也就是Annotation注解。

    注解和Python的装饰器一样的功能,在代码里添加标记,在编译、类加载、运行时被读取,并执行相应的处理。通过注解可以在不改变原有逻辑的情况下,在源文件里添加一些信息。

    • Annotation 为程序元素(类,方法,成员变量)设置元数据。
    • Annotation 增加删除不影响代码的执行
    • 希望Annotation起作用,只能通过APT(Annotation Processing Tool)工具来访问和处理Annotation的信息。

    8.1)基本Annotation

    • @Override

      ​ 限定重写父类方法,保证父类中有一个和该方法重名的方法。

    • @Deprecated

      ​ 标示某个类或者方法已过时。继续使用会有编译器警告

      @Deprecated
      public static void getInfo() {
        System.out.println("");
      }
      
    • @SuppressWarnings

      抑制编译器警告

      //  如果是为value设置,可以省略value
      @SuppressWarnings("unchecked")
      // 为Annotation成员变量value设值
      @SuppressWarnings(value = "unchecked")
      
    • @SafeVarags

      堆污染警告

    • @FuncationalInterface

      指定某个接口必须是函数式接口。(接口中只有一个抽象方法,可以包含多个默认方法或多个static 方法,就是函数式接口)

      @FunctionalInterface
      public interface FunInterface{
          static void foo(){
              System.out.println("类方法");
          }
      
          default void bar(){
              System.out.println("默认方法");
          }
      
          // 只定义一个抽象方法
          void test();
      }
      

    9、IO

    输入流和输出流

    • 输入流:只能从中读取数据,而不能向其写入数据。(字节输入流InputStream 字符输入流Reader)
    • 输出流:只能向其写入数据,而不能从中去读数据。(字节输出流OutputStream 字符输出流Writer)

    10、多线程

    并行和并发

    • 并行:指两个或多个事件在同一时刻点发生
    • 并发:指两个或多个事件在同一时间段内发生

    10.1)线程的创建和启动

    10.1.1)继承Thread类创建线程类

    所有的线程都必须是Thread类或其子类的实例。

    1. 定义Thread类,并重写run()方法,run()方法体代表了线程需要完成的任务。
    2. 创建Thread子类实例
    3. 调用实例的start()方法启动该线程
    new Thread() {
        @Override
        public void run() {
            for (int i = 0; i < 100; i++) {
                System.out.println("thread name " + this.getName() + i);
            }
        }
    }.start();
    
    // 当前运行的线程
    Thread.currentThread()
    // 获取当前线程的名字
    .getName() 
    // 设置当前线程的名字
    setName(name)
    

    10.1.2)实现Runnable接口创建线程类

    • 定义实现Runnable的类,重写接口的run()方法,run()同样是线程的执行体
    • 创建Runnable实现类的实例,并以此实例作为 Thread的 target来创建Thread对象,该Thread对象才是真正的线程对象。
    new Thread(new Runnable() {
        @Override
        public void run() {
            for (int i = 0; i < 100; i++) {
                System.out.println("thread Name " + Thread.currentThread().getName() + i);
            }
        }
    }).start();
    

    Runnable接口创建的多线程,可以共享线程的实例变量。

    10.1.3)使用Callable和Future创建线程

    Callable相当于Runnable的增强版。里面的call()方法可以作为线程的执行体。call()更强大。

    • call()f方法可以有参数。
    • call()方法可以声明抛出异常。

    Callable对象不能直接作为Thread的target。

    // FutureTask 包装 Callable,FutureTask继承Runnable
    FutureTask<Integer> futureTask = new FutureTask<>(new Callable<Integer>() {
        @Override
        public Integer call() throws Exception {
            for (int i = 0; i < 100; i++) {
                System.out.println("current thread name " + Thread.currentThread().getName() + " " + i);
            }
            return 1000;
        }
    });
    
    new Thread(futureTask).start();
    try {
        // 获取返回值
        System.out.println(futureTask.get());
    } catch (InterruptedException e) {
        e.printStackTrace();
    } catch (ExecutionException e) {
        e.printStackTrace();
    }
    

    10.2)线程的生命周期

    启动线程用start()方法,而不是run()方法。直接调用run()方法是不是启动线程的。也不要连续调用两次start()方法。

    [图片上传失败...(image-51f2bf-1571732324683)]

    10.3)控制线程

    10.3.1) join

    public class JoinThread extends Thread {
        @Override
        public void run() {
            super.run();
            for (int i = 0; i < 100; i++) {
                System.out.println("JoinThread " + i);
            }
        }
    
        public static void main(String[] args) {
            for (int i = 0; i < 100; i++) {
                if (i == 20) {
                    JoinThread joinThread = new JoinThread();
                    joinThread.start();
                    try {
                      // 在main线程中调用了joinThread的join()方法,main线程必须等待joinThread执行结束才会向下执行
                        joinThread.join();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                System.out.println("Main Thread " + i);
            }
        }
    }
    

    10.3.2)后台线程(Daemon Thread)

    在后台运行,为其他线程提供服务。

    特征:所有的前台线程都死亡了,后台线程会自动死亡。

    调用Thread对象setDaemon(true) 方法可以将指定的线程设置成后台线程。

    public class DaemonThread extends Thread {
        @Override
        public void run() {
            for (int i = 0; i < 1000; i++) {
                System.out.println(getName() + i);
            }
        }
    
        public static void main(String[] args) {
            DaemonThread daemonThread = new DaemonThread();
            // 在start之前指定线程设置成后台线程
            daemonThread.setDaemon(true);
            daemonThread.start();
            for (int i = 0; i < 4; i++) {
                System.out.println(Thread.currentThread().getName() + i);
            }
        }
    }
    

    isDaemon() 判断线程是否为后台线程。

    前台线程死亡后,JVM会通知后台线程死亡,但它收到指令做出响应,需要一定时间。

    10.3.3)线程睡眠:sleep

    Thread.sleep(1000) 静态方法让当前执行的线程休眠。

    public class SleepThread {
        public static void main(String[] args) throws InterruptedException {
            for (int i = 0; i < 10; i++) {
                System.out.println("当前时间:" + new Date());
                Thread.sleep(1000);
            }
        }
    }
    

    10.3.4)线程让步:yield

    yield()方法和sleep()方法相似。也是Thread的静态方法,他可以让当前线程停止,但不会阻塞当前线程,他是将线程转入就绪状态。

    public class YieldThread extends Thread {
        private YieldThread(String name) {
            super(name);
        }
    
        @Override
        public void run() {
            for (int i = 0; i < 50; i++) {
                System.out.println(getName() + i);
                if (i == 20) {
                    Thread.yield();
                }
            }
        }
    
        public static void main(String[] args) {
            YieldThread yieldThread = new YieldThread("高级");
            yieldThread.setPriority(Thread.MAX_PRIORITY);
            yieldThread.start();
    
            YieldThread yieldThread1 = new YieldThread("低级");
            yieldThread1.setPriority(Thread.MIN_PRIORITY);
            yieldThread1.start();
        }
    }
    
    

    sleep比yield有更好的可移植性。

    10.3.5)改变线程优先级

    每个线程都有优先级,默认和创建它的父线程的优先级相同。优先级高获得更多的执行机会。main线程具有普通优先级。

    thread.setPriority()
    int priotity = yieldThread.getPriority();
    public final static int MIN_PRIORITY = 1;
    public final static int NORM_PRIORITY = 5;
    public final static int MAX_PRIORITY = 10;
    

    10.4)线程同步

    两个线程并发访问修改同一个文件,就可能造成异常。这就是线程安全问题。

    10.4.1)同步代码块

    synchronized(obj){
      // 同步代码块
    }
    

    obj是同步监视器,线程开始执行同步代码块之前,必须先获得同步监视器的锁定。任何时刻只能有一个线程可以获得对同步监视器的锁定,当同步代码执行完成之后,该线程会释放对该同步监视器的锁定。

    Java允许任何对象作为同步监视器的锁定,但建议将并发访问的共享资源作为同步监视器。

    public class SysncThread extends Thread {
        private Account account;
        private int money;
    
        public SysncThread(String name, Account account, int money) {
            super(name);
            this.account = account;
            this.money = money;
        }
    
        @Override
        public void run() {
          // 符合加锁 -- 修改 -- 释放锁的逻辑,一般是要操作的对象作为同步监视器。
            synchronized (account) {
            for (int i = 0; i < 10; i++) {
                if (account.getMoney() > money) {
                    account.removeMoney(money);
                    System.out.println("当前余额:" + account.getMoney());
                } else {
                    System.out.println("余额不足,取钱失败");
                }
            }
            }
        }
    
        public static void main(String[] args) {
            Account account = new Account(1000);
            new SysncThread("A", account, 100).start();
            new SysncThread("B", account, 100).start();
        }
    }
    
    class Account {
        private int money;
    
        public int getMoney() {
            return money;
        }
    
        public void removeMoney(int money) {
            this.money -= money;
        }
    
        public Account(int money) {
            this.money = money;
        }
    }
    

    10.4.2)同步方法

    同步方法就是使用synchronized关键字来修饰某个方法,则该方法成为同步方法。被synchronized修饰的方法,无须指定同步监视器,同步方法的同步监视器就是this。

    public class SysncThread extends Thread {
        private Account account;
        private int money;
        private Object object = new Object();
    
        public SysncThread(String name, Account account, int money) {
            super(name);
            this.account = account;
            this.money = money;
        }
    
        // 可以去掉同步代码块了
        @Override
        public void run() {
            for (int i = 0; i < 10; i++) {
                account.removeMoney(money);
            }
        }
    
        public static void main(String[] args) {
            Account account = new Account(1000);
            new SysncThread("A", account, 100).start();
            new SysncThread("B", account, 100).start();
        }
    }
    
    class Account {
        private int money;
    
        public int getMoney() {
            return money;
        }
    
        // 同步方法:多线程操作访问的方法
        public synchronized void removeMoney(int money) {
            if (this.money >= money) {
                this.money -= money;
                System.out.println("当前余额:" + this.money);
            } else {
                System.out.println("当前余额不足");
            }
        }
    
        public Account(int money) {
            this.money = money;
        }
    }
    

    synchronized尽量减少作用域。

    可变类的线程安全是降低程序运行效率为代价的。为了减少线程安全带来的负面影响,程序可以采用的策略。

    • 不要对线程安全类的所有方法都进行同步,只对那些会改变竞争资源的方法同步。
    • 如果可变类有两种运行环境,单线程和多线程,则应为该可变类提供两种版本,即线程不安全版本和线程安全版本。

    10.4.3)释放同步监视器的锁定

    同步代码块同步方法在执行之前必须先获得同步监视器的锁定。如何释放同步监视的锁定呢。

    • 同步方法,同步代码块执行结束
    • 同步方法,同步代码块遇到break return终止了该代码块方法的执行
    • 同步方法,同步代码块出现了未处理的Error或Exception,导致方法,代码块异常结束
    • 执行同步代码块,同步方法时,程序执行了同步监视器对象的wait()方法,则当前线程暂停,并释放同步监视器。

    如何几种情况线程不会释放同步监视器

    • 执行同步代码块同步方法时,调用了Thread.sleep() Thread.yield()方法来暂停当前线程的执行,不会释放同步监视器。
    • 线程执行了同步代码块是,其他线程调用了suspend()方法将该线程挂机,该线程不会释放同步监视器。程序硬尽量避免使用suspend() resume()方法来控制线程。

    10.4.4)同步锁(Lock)

    通过显示定义同步锁对象来实现同步。
    Lock提供了比同步方法,同步代码块更广泛的操作,更灵活的结果。

    ReadWriteLock(读写锁),ReentrantLock(可重入锁),StampedLock类。

    class Account {
            //定义锁
        private final ReentrantLock lock = new ReentrantLock();
        private int money;
    
        public int getMoney() {
            return money;
        }
    
        // 同步方法:多线程操作访问的方法
        public void removeMoney(int money) {
                // 加锁
            lock.lock();
            try {
                if (this.money >= money) {
                    this.money -= money;
                    System.out.println("当前余额:" + this.money);
                } else {
                    System.out.println("当前余额不足");
                }
            } finally {
                    // 释放锁
                lock.unlock();
            }
        }
    
        public Account(int money) {
            this.money = money;
        }
    }
    

    使用ReentrantLock对象来进行同步,加锁和释放所出现在不同的作用范围内时,建议用finally块来确保必要时释放锁。

    10.5)volatile关键字

    volatile关键字为实例的同步访问提供了免锁机制。如果一个filed声明为volatile,那么编译器和虚拟机就知道改filed可能是被另一个线程并发更新。

    10.5.1)Java内存模型

    • 堆内存
      堆内存存储对象实例,是被线程共享的运行时内存区域。存在内存可见性问题。而局部变量,方法定义的参数则不会在线程之间共享,不会有内存可见问题。

    • 主存

      java内存模型定义了线程和主存之间的抽象关系,线程之间共享变量存储在主存中,同时每个线程都有一个私有的本地内存,本地内存存储了该线程共享变量的副本。

      [图片上传失败...(image-a05765-1571732324683)]

    10.5.2)原子性,可见性,有序性

    • 原子性

      对基本数据类型变量的读取和赋值操作时原子性操作,即这些操作是不可被中断的。

      x = 3; // 原子操作
      y = x; // 先读取x再赋值,不是原子性操作
      x++; // 也不是原子操作
      

      只有简单的读取和赋值才是原子性操作。

    • 可见性
      一个线程修改的状态另一个线程立马就能看到。

      通常情况下,线程修改一个变量,并不会立即写入主存,何时写入主存也是不确定的,当其他线程读取改值是,此时主存中可能还是原来的旧值。

      但是volatile修饰的变量,能保证修改的值立即被更新到主存,所以对其他线程是可见的。

    • 有序性
      java内存模型中允许编译器和处理器对指令进行重排序。volatile、synchronized、Lock保证每个时刻只有一个线程执行同步代码。相当于让线程顺序执行代码,从而保证了有序性。

    volatile,不保证原子性,保证有序性和可见性。

    volatile 在保证有序性方面的性能要高于synchronized,volatile是修饰一个filed的,s'ynchronized修饰一段代码或函数。

    10.5)线程通信

    10.5.1)传统线程通信

    借助Object类提供的 wait() notify() notifyAll() 三个方法。这三个方法必须由同步监视器对象来调用。

    • synchronized 修饰的同步方法,同步监视器是是this,所以可以在同步方法中直接调用这三个方法
    • synchronized 修饰的代码块,同步监视器是synchronized括号里对象,所以必须使用这个对象调用这三个方法
    wait(): 导致当前线程等待,直到调用了notify() notifyAll() 方法。wait()可以带时间参数,表示经过这么长时间会自动唤醒。
    notify(): 唤醒此同步监视器上等待的线程,唤醒那个线程是任意的。
    notifyAll(): 唤醒此同步监视器上等待的所有线程。
    
    class Account {
        private int money;
        private boolean flag;
    
        public Account(int money) {
            this.money = money;
        }
    
        public int getMoney() {
            return money;
        }
    
        public synchronized void draw(int drawAmount) {
            try {
                if (!flag) {
                    wait();
                } else {
                    System.out.println(Thread.currentThread().getName() + "取钱:" + drawAmount);
                    money -= drawAmount;
                    System.out.println("余额:" + money);
                    flag = false;
                    notifyAll();
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    
        public synchronized void deposit(int depositAmount) {
            try {
                if (flag) {
                    wait();
                } else {
                    System.out.println(Thread.currentThread().getName() + "存钱操作" + depositAmount);
                    money += depositAmount;
                    System.out.println("余额:" + money);
                    flag = false;
                    notifyAll();
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
    

    10.5.2) 使用Condition控制线程通信

    如果使用synchronized关键字保证同步,而是用Lock保证同步,则系统中不存在同步监视器,也就不能用wait() notify() notifyAll() 方法进行线程通信了。

    使用Lock对象保证线程同步,可以使用Condition对象来保持协调。
    Condition将同步监视器方法 wait() notify() notifyAll() 分解成截然不同的对象。

    Condition对象绑定在Lock对象上,可以 lock.newCondition()来获得Conditaion对象。

    Condition对象的方法

    await(): 类似同步监视器的 wait() 方法,也有类似的等待多久的await()方法。
    sigal(): 唤醒在此Lock对象上等待的单个线程。选择哪个一线程是任意的。
    sigalAll(): 唤醒在此Lock对象上等待的所有线程。
    
    ​```java
    class Account {
            private Lock lock = new ReentrantLock();
            private Condition condition = lock.newCondition();
            private int money;
            private boolean flag;
    
            public Account(int money) {
                this.money = money;
            }
    
            public int getMoney() {
                return money;
            }
    
            public void draw(int drawAmount) {
                lock.lock();
                try {
                    if (!flag) {
                        condition.wait();
                    } else {
                        System.out.println(Thread.currentThread().getName() + "取钱:" + drawAmount);
                        money -= drawAmount;
                        System.out.println("余额:" + money);
                        flag = false;
                        condition.signalAll();
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                } finally {
                    lock.unlock();
                }
            }
    
            public void deposit(int depositAmount) {
                lock.lock();
                try {
                    if (flag) {
                        condition.await();
                    } else {
                        System.out.println(Thread.currentThread().getName() + "存钱" + depositAmount);
                        money += depositAmount;
                        System.out.println("余额:" + money);
                        flag = true;
                        condition.notifyAll();
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    lock.unlock();
                }
            }
        }
    

    10.5.3) 使用阻塞队列(BlockingQueue)控制线程通信

    生产者向BlockingQueue中添加元素,如果队列已满,则会阻塞。
    消费者向BlockingQueue中消费元素,如果队列已空,则会阻塞。

    抛出异常 不同返回值 阻塞线程 指定超时时长
    队尾插入元素 add(e) offer(e) put(e) offer(e, time,unit)
    队头删除元素 remove(e) poll take() poll(time,unit)
    获取,不删除元素 element() peek()
    • ArrayBlockingQueue:数组实现的
    • LinkedBlockingQueue:链表实现
    • PriorityBlockingQueue:不是标准的阻塞队列,
    • SynchronousQueue:同步队列,存取必须交替进行
    • DelayQueue
    public class BlockQueue {
        public static void main(String[] args) throws InterruptedException {
            BlockingQueue<String> blockingQueue = new ArrayBlockingQueue<>(2);
            blockingQueue.put("java");
            blockingQueue.put("java");
            blockingQueue.put("java");
            System.out.println(blockingQueue.size());
        }
    }
    
    public class BlockQueue {
        public static void main(String[] args) throws InterruptedException {
            BlockingQueue<String> blockingQueue = new LinkedBlockingDeque<>(1);
            new Producer(blockingQueue).start();
            new Producer(blockingQueue).start();
            new Producer(blockingQueue).start();
            new Consumer(blockingQueue).start();
        }
    }
    
    class Producer extends Thread {
        private BlockingQueue<String> blockingQueue;
    
        public Producer(BlockingQueue<String> blockingQueue) {
            this.blockingQueue = blockingQueue;
        }
    
        @Override
        public void run() {
            String[] strings = new String[]{
                    "java",
                    "Structs",
                    "Spring",
            };
            for (int i = 0; i < 100; i++) {
                System.out.println(getName() + "生产者准备生产");
                try {
                    Thread.sleep(200);
                    blockingQueue.put(strings[i % 3]);
                } catch (Exception e) {
                    e.printStackTrace();
                }
                System.out.println(getName() + "生产完成" + blockingQueue);
            }
        }
    }
    
    class Consumer extends Thread {
        private BlockingQueue<String> blockingQueue;
    
        public Consumer(BlockingQueue<String> blockingQueue) {
            this.blockingQueue = blockingQueue;
        }
    
        @Override
        public void run() {
            while (true) {
                System.out.println(getName() + "消费者准备消费集合元素");
                try {
                    Thread.sleep(200);
                    blockingQueue.take();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(getName() + "消费完成:" + blockingQueue);
            }
        }
    }
    

    10.6)线程组

    线程组可以对一批线程进行分类管理,Java允许对线程组进行控制。如果没有显示指定线程属于哪个线程组,则该线程组属于默认线程组。子线程和创建它的父线程处于同一个线程组。一旦加入一个线程组,就一直属于这个线程组。

    创建线程时指定线程组

    Thread(ThreadGroup group, Runnable target)
    Thread(ThreadGroup group, Runnable target, String name) 
    Thread(ThreadGroup group, String name)
    

    设置了线程组后中途不能改变。只有getThreadGroup()没有setThreadGroup()
    创建线程组

    // 指定线程组的名字
    ThreadGroup(String name) 
    // 指定父线程组和线程组的名字
    ThreadGroup(ThreadGroup parent, String name)
    

    线程组默认实现了线程异常处理类接口,当有子线程没有处理异常,则线程组会捕获该异常来处理。

    ThreadGroup类提供了几个常用方法来操作整个线程组里的所有线程。

    int activeCount(); // 活动线程数
    interrupt(); //  中断线程
    isDaemon(); // 判断线程组是否是后台线程
    setDaemon(boolean daemon); // 设置为后台线程
    setMaxPriority(int pri); // 设置线程组的最高优先级
    

    10.7) 线程池

    10.7.1) 线程池原理和好处

    线程出现的原因:
    系统启动一个新线程的成本是比较高,因为涉及与操作系统的交互,所以使用线程池可以很好提高程序的性能,尤其是程序需要创建大量生存期很短的线程,更应该考虑线程池。

    线程池的原理:
    与数据库连接池类似,线程池在系统启动时创建大量的空闲线程,程序将Runnable对象 Callable对象传递给线程池,线程池就会启动一个线程来执行他们的run()或者call()方法,当run()或者call()方法执行结束后,该线程并不会死亡,而是再次返回线程池中成为空闲状态,等待执行下一个Runnable对象的run()或call()方法。

    线程池的好处

    • 提高性能
    • 有效控制系统中并发线程的数量,当系统中包含大量并发线城时,会导致系统性能剧烈下降,甚至导致JVM崩溃,线程池的最大线程数可以控制并发线程数不超过次数。

    10.7.2)线程池的创建

    // 创建一个具有缓存功能线程池
    Executors.newCachedThreadPool();
    
    // 创建一个固定个数线程的线程池
    Executors.newFixedThreadPool(4);
    Executors.newSingleThreadExecutor();
    
    // 创建指定个数的线程池,在指定延迟后执行线程任务
    Executors.newScheduledThreadPool(6);
    Executors.newSingleThreadScheduledExecutor();
    
    // 创建持有足够的线程的线程池,会使用多个队列来减少竞争
    Executors.newWorkStealingPool(4);
    Executors.newWorkStealingPool();
    

    前三个方法返回一个ExecutorService对象,该对象是一个线程池,可以执行Runnable对象和Callable对象。后两个返回ScheduledExecutorService。

    ExecutorService尽快执行线程的线程池。
    可执行的方法

    // 无返回值
    Future<?> submit(Runnable task)
    // 用result显示指定返回的结果,所以返回result
    <T> Future<T> submit(Runnable task,T result) 
    
    ExecutorService executorService = Executors.newFixedThreadPool(6);
        Runnable targer = () -> {
            for (int i = 0; i < 100; i++) {
                System.out.println(Thread.currentThread().getName() + "的值为" + i);
            }
        };
        executorService.submit(targer);
        executorService.submit(targer);
        executorService.submit(targer);
        executorService.shutdown();
    

    用完一个线程池后,应该调用该线程池的shutdown()方法,该方法将启动线程池的关闭序列,调用shutdown()后不再接受新任务。

    10.7.3)增强的ForkJoinPool

    ForkJoinPool支持多CPU,是ExecutorService的实现类。

    常用的构造器

    // 创建10个并行线程的线程池 10个是CPU个数
    ForkJoinPool forkJoinPool = new ForkJoinPool(10);
    
    // 可用CPU个数作为参数
    ForkJoinPool forkJoinPool = new ForkJoinPool(Runtime.getRuntime().availableProcessors());
    
    // 返回一个通用线程池,通用池的运行状态不受shutdown()或shutdownNow()影响
    ForkJoinPool forkJoinPool = ForkJoinPool.commonPool();
    
    // 返回通用池的并行级别
    int commonPoolParallelism = ForkJoinPool.getCommonPoolParallelism();
    

    创建ForkJoinPool之后就可以调用submit(ForkJoinTask task)或invoke(ForkJoinTask task)方法来执行指定的任务。

    ForkJoinTask代表一个可以并行,合并的任务。他的两个抽象子类 RecursiveAction和RecursiveTask。其中RecursiveTask代表有返回值的任务,RecursiveAction没有返回值的任务。

    public class ForkJoinPoolThread {
        public static void main(String[] args) {
            ForkJoinPool pool = new ForkJoinPool();
            pool.submit(new PrintTask(0, 300));
            try {
                pool.awaitTermination(2, TimeUnit.SECONDS);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            pool.shutdown();
        }
    }
    
    class PrintTask extends RecursiveAction {
    
        // 每个小任务最多只能打印50个数
        private static final int THRESHOLD = 50;
        private int start;
        private int end;
    
        public PrintTask(int start, int end) {
            this.start = start;
            this.end = end;
        }
    
        @Override
        protected void compute() {
            if (end - start < THRESHOLD) {
                for (int i = start; i < end; i++) {
                    System.out.println(Thread.currentThread().getName() + "的i值:" + i);
                }
            } else {
                // 将大任务分解
                int middle = (start + end) / 2;
                PrintTask left = new PrintTask(start, middle);
                PrintTask right = new PrintTask(middle, end);
                
                // 并行执行多个小任务
                left.fork();
                right.fork();
            }
        }
    }
    
    public class ForkJoinPoolThread {
        public static void main(String[] args) throws ExecutionException, InterruptedException {
            int arr[] = new int[100];
            Random random = new Random();
            int total = 0;
            for (int i = 0, len = arr.length; i < len; i++) {
                int tmp = random.nextInt(20);
                total += (arr[i] = tmp);
            }
            System.out.println(total);
            ForkJoinPool forkJoinPool = ForkJoinPool.commonPool();
            Future<Integer> future = forkJoinPool.submit(new CalTask(arr, 0, arr.length));
            System.out.println(future.get());
        }
    }
    
    class CalTask extends RecursiveTask<Integer> {
        private static final int THRESHOLD = 20;
        private int arr[];
        private int start;
        private int end;
    
        public CalTask(int[] arr, int start, int end) {
            this.arr = arr;
            this.start = start;
            this.end = end;
        }
    
        @Override
        protected Integer compute() {
            int sum = 0;
            if (end - start < THRESHOLD) {
                for (int i = start; i < end; i++) {
                    sum += arr[i];
                }
                return sum;
            } else {
                int middle = (start + end) / 2;
                CalTask left = new CalTask(arr, start, middle);
                CalTask right = new CalTask(arr, middle, end);
                // 并行执行小任务
                left.fork();
                right.fork();
                // 将小任务的结果合并起来
                return left.join() + right.join();
            }
        }
    }
    

    10.7.4)THreadPoolExecutor(重点)

    Executor框架中最核心的成员是ThreadPoolExecutor。他是线程池的核心实现类。

    mThreadPoolExecutor = new ThreadPoolExecutor(
            int corePoolSize,
            int maximumPoolSize,
            long keepAliveTime,
            TimeUnit unit,
            BlockingQueue<Runable> workQueue
            ThreadFactory threadFactory,
                    RejectedExecutorHandler handler);
    
    mThreadPoolExecutor.allowCoreThreadTimeOut(true);
    

    ①int coreSize : 核心线程数(不会被回收)。

    ②int maxSize : 最大线程数。

    ③long KeepAliveTime : 膨胀出来的线程回收时间。

    ④TimeUnit unit : 时间单位。(通过TimeUnit.时间单位调用)

    ⑤BlockingQueue queue : 线程的阻塞队列。(必须有界)

    ⑥ThreadFactory factory : 产生线程的工厂。

    ⑦RejectedExecutionHandler handler : 当线程大于总数(最大线程数 + 阻塞队列)时,将由handler拒绝任务。

    ![image-20190811143946990](/Users/wangbo/Library/Application Support/typora-user-images/image-20190811143946990.png)

    ![image-20190812173842577](/Users/wangbo/Library/Application Support/typora-user-images/image-20190812173842577.png)

    如果我们执行ThreadPoolExecutor的execute方法,会遇到各种情况:

    (1) 如果线程池中的线程数未达到核心线程数,则创建核心线程处理任务。
    (2)如果线程数大于或者等于核心线程数,则将任务加入任务队列,线程池中的空闲线程会不断地从任务队列中取出任务进行处理。
    (3)如果任务队列满了,并且线程数没有达到最大线程数,则创建非核心线程去处理任务。
    (4)如果线程数超过了最大线程数,则执行饱和策略。
    

    例子

    package com.cari.cari.promo.diskon.util;
    
    import android.os.Handler;
    import android.os.Looper;
    
    import com.cari.promo.diskon.BuildConfig;
    import com.crashlytics.android.Crashlytics;
    
    import java.util.concurrent.Future;
    import java.util.concurrent.LinkedBlockingQueue;
    import java.util.concurrent.ThreadFactory;
    import java.util.concurrent.ThreadPoolExecutor;
    import java.util.concurrent.TimeUnit;
    
    public enum ThreadUtil {
        Database(2),
        General(4),
        Network(6);
    
        private static final Handler MAIN_HANDLER = new Handler(Looper.getMainLooper());
    
        public static boolean checkIsInMainThread() {
            return Looper.myLooper() == Looper.getMainLooper();
        }
    
        public static void confirmInMainThread() {
            if (BuildConfig.DEBUG && (!checkIsInMainThread())) {
                throw new RuntimeException("Confirm Ui Thread Error!");
            }
        }
    
        public static void runInMainThread(boolean alwaysPost, Runnable runnable) {
            if (runnable == null) {
                return;
            }
    
            if ((!alwaysPost) && checkIsInMainThread()) {
                runnable.run();
            } else {
                MAIN_HANDLER.post(runnable);
            }
        }
    
        public static void runInMainThreadDelayed(Runnable runnable, long delayMillis) {
            if (runnable == null) {
                return;
            }
    
            MAIN_HANDLER.postDelayed(
                    runnable,
                    delayMillis < 0 ? 0 : delayMillis);
        }
    
        private final ThreadPoolExecutor mThreadPoolExecutor;
    
        ThreadUtil(int poolSize) {
            if (poolSize < 1) {
                poolSize = 1;
            }
    
            mThreadPoolExecutor = new ThreadPoolExecutor(
                    poolSize,
                    poolSize,
                    30,
                    TimeUnit.SECONDS,
                    new LinkedBlockingQueue<Runnable>(),
                    new ThreadFactory() {
                        @Override
                        public Thread newThread(Runnable r) {
                            return new Thread(r, "ThreadUtil-" + name());
                        }
                    });
    
            mThreadPoolExecutor.allowCoreThreadTimeOut(true);
        }
    
        public Future<?> execute(Runnable runnable) {
            return mThreadPoolExecutor.submit(new RunnableTask(runnable));
        }
    
        private static final class RunnableTask implements Runnable {
            private final Runnable mRunnable;
    
            private RunnableTask(Runnable runnable) {
                mRunnable = runnable;
            }
    
            @Override
            public void run() {
                try {
                    if (mRunnable != null) {
                        mRunnable.run();
                    }
                } catch (Throwable t) {
                    if (checkIsInMainThread()) {
                        throw t;
                    } else {
                        final RuntimeException exception = new RuntimeException("Sub-Thread throws exception!", t);
                        if (BuildConfig.DEBUG) {
                            // 让主线程也崩溃
                            runInMainThread(false, new Runnable() {
                                @Override
                                public void run() {
                                    throw exception;
                                }
                            });
                        } else {
                            // 记录到统计平台
                            runInMainThread(false, new Runnable() {
                                @Override
                                public void run() {
                                    try {
                                        Crashlytics.logException(exception);
                                    } catch (Throwable ignore) {
                                    }
                                }
                            });
                        }
                    }
                }
            }
        }
    }
    
    

    10.8 线程池的种类

    通过直接或间接配置ThreadPoolExecutor的参数可以创建不同类型的ThreadPoolExecutor。

    4种线程池比较常用、FixedThreadPool CachedThreadPool SingleThreadExecutor ScheduledThreadPool

    这几个线程池都是Executors 类中的静态方法生成的。

    10.8.1)FixedThreadPool

    public static ExecutorService newFixedThreadPool(int nThreads, ThreadFactory threadFactory) {
            return new ThreadPoolExecutor(nThreads, nThreads,
                                          0L, TimeUnit.MILLISECONDS,
                                          new LinkedBlockingQueue<Runnable>(),
                                          threadFactory);
        }
    

    10.8.2)CachedThreadPool

     public static ExecutorService newCachedThreadPool(ThreadFactory threadFactory) {
            return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                          60L, TimeUnit.SECONDS,
                                          new SynchronousQueue<Runnable>(),
                                          threadFactory);
        }
    

    10.8.3) SingleThreadExecutor

     public static ExecutorService newSingleThreadExecutor(ThreadFactory threadFactory) {
            return new FinalizableDelegatedExecutorService
                (new ThreadPoolExecutor(1, 1,
                                        0L, TimeUnit.MILLISECONDS,
                                        new LinkedBlockingQueue<Runnable>(),
                                        threadFactory));
        }
    

    10.8.4) ScheduleThreadPool

     public static ScheduledExecutorService newScheduledThreadPool(
                int corePoolSize, ThreadFactory threadFactory) {
            return new ScheduledThreadPoolExecutor(corePoolSize, threadFactory);
        }
    
    

    10.9)线程相关类

    ThreadLocal类

    • 线程局部变量
    • 把数据放到ThreadLocal中可以让每个线程创建一个该变量的一个副本,从而避免并发访问线程安全问题。

    ThreadLocal的方法

    // 放回当前线程中副本中的值
    T get()
    // 删除此线程局部变量中当前线程的值
    void remove()
    // 设置次线程局部变量中当前副本的值
    void set(T value)
    
    public class ThreadLocalVar extends Thread {
        private Acount acount;
    
        public ThreadLocalVar(Acount acount, String name) {
            super(name);
            this.acount = acount;
        }
    
        @Override
        public void run() {
            for (int i = 0; i < 10; i++) {
                if (i == 6) {
                    acount.setName(getName());
                }
                System.out.println(acount.getName() + "账户I的值" + i);
            }
        }
    
        public static void main(String[] args) {
            Acount acount = new Acount("初始名");
            new ThreadLocalVar(acount, "线程A").start();
            new ThreadLocalVar(acount, "线程B").start();
        }
    }
    
    class Acount {
        private ThreadLocal<String> name = new ThreadLocal<>();
    
        public Acount(String str) {
            this.name.set(str);
            // 访问当前线程的name的副本
            System.out.println(this.name.get());
        }
    
        public String getName() {
            return this.name.get();
        }
    
        public void setName(String str) {
            this.name.set(str);
        }
    }
    

    11、类加载和反射

    11.1) 类加载、链接和初始化

    11.1.1)JVM和类

    当调用java命令运行程序时,会启动一个JVM,同一个JVM下的所有线程,变量都处于同一个进程中,都使用该JVM进程内存区。不同JVM不共享内存区。

    JVM终止的情况

    • 程序运行到最后。
    • 使用System.exit()Runtime.getRuntime().exit()代码处结束程序。
    • 程序执行过程中遇到未捕获的异常或错误而结束。
    • 程序所在平台强制结束了JVM进程。

    11.1.2)类加载

    第一次使用某个未被加载到内存中的类时,系统会通过加载、连接、初始化三个步骤对类进行初始化。

    类加载: 是指将类的class文件读入内存,并为之创建一个java.lang.Class对象。

    类的加载由加载器完成,可以自定义加载器。

    11.1.3)类的连接

    连接负责把类的二进制数据合并到JRE中。

    • 验证:校验被加载的类是否有正确的内部结构,并和其他类协调一致。
    • 准备:负责为类的类变量分配内存,并设置默认初始值。
    • 解析:类的二进制数据中的符号引用替换成直接引用。

    11.1.4)类的初始化

    虚拟机对类进行初始化。

    1. 声明类变量时指定的初始化值
    2. 静态初始化块为类变量指定初始值
    public class Test {
        // 声明时初始化
        static int a = 4;
        // 默认值
        static int b;
        static int c;
    
        // 静态初始化块
        static {
            b = 6;
        }
    }
    

    类的初始化步骤:

    1. 如果类没有被加载和连接,则程序先加载并连接该类
    2. 如果该类的直接父类还没有初始化,则先初始化其直接父类
    3. 如果类中有初始化语句,则系统一次执行这些初始化语句。

    11.1.5)类的初始化时机

    1. 创建类的实例。
    2. 调用某个类的类方法。
    3. 访问某个类或接口的类变量,或为该类变量赋值。
    4. 使用反射方式来强制创建某个类或接口对应的java.lang.Class对象。
    5. 初始化某个累的子类。
    6. 直接使用java.exe命令来运行某个主类。

    类变量使用final修饰,它的值在编译时就能确定,在使用该变量时相当于宏替换。

    ClassLoader classLoader = ClassLoader.getSystemClassLoader();
    try {
        classLoader.loadClass("com.company.baseLib.Tester");
    } catch (ClassNotFoundException e) {
        e.printStackTrace();
    }
    System.out.println("系统加载Tester类");
    try {
        Class.forName("com.company.baseLib.Tester");
    } catch (ClassNotFoundException e) {
        e.printStackTrace();
    }
    

    loadClass只是加载并没有初始化,forName做了初始化。

    11.2)类加载器

    类加载器负责将.class文件加载到内存中,并为之生成对应的java.lang.Class对象。同一个类不会被加载两次。JVM中的类有唯一的标识。全限定类名和加载器负责加载。不同加载器加载同类名是不同的标识。

    • Bootstrap ClassLoader:根类加载器
    • Extension ClassLoader:扩展类加载器
    • System ClassLoader:系统类加载器

    11.3)反射查看类信息

    编译时类型和运行时类型。

    Person p = new Student();
    // 编译时 Person,运行时Student。
    

    11.3.1)获得Class对象

    • 字符串获取
    // 1. Class累的forName()静态方法
    Class.forName("com.company.baseLib.Tester");
    
    • 类获取
    // 2. 类的class属性
    Tester.class;
    
    • 对象获取
    // 3. 对象的getClass()方法
    Tester tester = new Tester();
    tester.getClass();
    

    推荐用第二种方式获取Class对象。

    • 代码更安全。编译阶段就可以检查要访问的class对象是否存在。
    • 程序性能更好。因为无需调用方法,所以性能更好。

    11.3.2)从Class中获取信息

    ![image-20190713110505792](/Users/wangbo/Library/Application Support/typora-user-images/image-20190713110505792.png)

    • 包路径
    getPackage();
    
    • 类名称
    getName()
    
    • 继承类
    getSuperclass()
    
    • 实现接口
    getInterfaces()
    
    • 获取Class对应类包含的构造器Constructor

      // 带指定参数列表的public构造器
      public Constructor<T> getConstructor(Class<?>... parameterTypes)
      // 所有public构造器
      public Constructor<?>[] getConstructors()
      // 带指定参数列表的构造器
      public Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes)
      // 所有构造器
      public Constructor<?>[] getDeclaredConstructors()
      

      Constructor的常用方法

      // 查看构造器方法是否允许带有可数量的参数
      isVarArgs()
      // 
      
    • 获取Class对应类所包含的方法

      // 带指定形参列表的public 方法
      public Method getMethod(String name, Class<?>... parameterTypes)
      
      // 所有的public方法
      public Method[] getMethods() 
      
      // 带指定形参列表的方法
      public Method getDeclaredMethod(String name, Class<?>... parameterTypes)
        
      // 所有方法
      public Method[] getDeclaredMethods() 
      
    • 获取Class对应类所包含的成员变量

      // 指定名称的public成员变量
      public Field getField(String name)
        
      // 所有public成员变量
      public Field[] getFields() 
      
      // 指定名称的成员变量
      public Field getDeclaredField(String name)
      // 所有成员变量
      public Field[] getDeclaredFields() 
      
    • 获取Class对应类所包含的Annotation

      // 指定类型的Annotation
      public <A extends Annotation> A getAnnotation(Class<A> annotationClass) 
        
      // 直接修饰Class对象的,指定类型Annotation
      public <A extends Annotation> A getDeclaredAnnotation(Class<A> annotationClass)
        
      // 
      

      example

      // Person 的Class
      Class<Person> clazz = Person.class;
      
      System.out.println("person 类的所有构造器:");
      Constructor[] constructors = clazz.getDeclaredConstructors();
      for (Constructor constructor : constructors) {
          System.out.println(constructor);
      }
      
      System.out.println("person 类的所有方法");
      Method[] methods = clazz.getDeclaredMethods();
      for (Method method : methods) {
          System.out.println(method);
      }
      
      System.out.println("获取Person类指定的方法");
      System.out.println("带一个字符串参数的info方法:" + clazz.getMethod("info", String.class));
      
      System.out.println("Person类的全部注解");
      Annotation[] annotations = clazz.getAnnotations();
      System.out.println("Person 的全部Annotation:");
      for (Annotation annotation : annotations) {
          System.out.println(annotation);
      }
      
      System.out.println("该Class元素上@SuppressWarning注解为:" + Arrays.toString(clazz.getAnnotationsByType(SuppressWarnings.class)));
      System.out.println("该Class元素上@Anno注解为:" + Arrays.toString(clazz.getAnnotationsByType(Anno.class)));
      
      System.out.println("Person类的内部类:");
      Class inClazz = Class.forName("com.company.classObj.Person$Inner");
      
      System.out.println("内部类对应的外部类:" + inClazz.getDeclaringClass());
      System.out.println("person对应的包:" + clazz.getPackage());
      System.out.println("person对应的父类:" + clazz.getSuperclass());
      

      11.4)反射生成并操作对象

      11.4.1)创建对象

      • newInstance() 创建实例,实际是利用默认构造器来创建该类的实例。

        Class clazz = Class.forName("com.company.classObj.Person");
        Person person = (Person) clazz.newInstance();
        System.out.println(person.age);
            ```
        
        
      • Class对象获取指定的Constructor对象,在调用Constructor对象的newInstance()方法来创建实例

        Class clazz = Class.forName("com.company.classObj.Person");
        // 构造器无需指定方法名,只需指定形参类型
        Constructor constructor = clazz.getConstructor(String.class);
        Object object = constructor.newInstance("指定的构造器");
        

      11.4.2)调用方法

      Person person = new Person();
      Class clazz = Person.class;
      // 指定方法名和形参类型
      Method method = clazz.getMethod("info", String.class);
      // 指定对象调用方法
      method.invoke(person,"调用info方法");
      

      11.4.3)访问成员变量

      
      public class Person {
          private String name;
          private int age;
      
          @Override
          public String toString() {
              return "Person{" +
                      "name='" + name + '\'' +
                      ", age=" + age +
                      '}';
          }
      
          public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException {
              Person p = new Person();
              Class<Person> personClass = Person.class;
      
              // 获取所有的指定名字Filed
              Field nameField = personClass.getDeclaredField("name");
              // 取消访问权限
              nameField.setAccessible(true);
              nameField.set(p, "wangbo");
              Field ageField = personClass.getDeclaredField("age");
              ageField.setAccessible(true);
              ageField.setInt(p, 30);
              System.out.println(p);
      
          }
      }
      
      

    相关文章

      网友评论

          本文标题:Java语法 1小时入门

          本文链接:https://www.haomeiwen.com/subject/eofqvctx.html