十三、Java高级特性 Lambda表达式 | 接口组成更新 | 方法引用 | 函数式接口

文章目录

十三、Java高级特性

1.Lambda表达式

1.1体验Lambda表达式【理解】

  • 案例需求

    启动一个线程,在控制台输出一句话:多线程程序启动了

  • 实现方式一

    • 实现步骤
      • 定义一个类MyRunnable实现Runnable接口,重写run()方法
      • 创建MyRunnable类的对象
      • 创建Thread类的对象,把MyRunnable的对象作为构造参数传递
      • 启动线程
  • 实现方式二

    • 匿名内部类的方式改进
  • 实现方式三

    • Lambda表达式的方式改进
  • 代码演示

    //方式一的线程类
    public class MyRunnable implements Runnable {
    
        @Override
        public void run() {
            System.out.println("多线程程序启动了");
        }
    }
    
    public class LambdaDemo {
        public static void main(String[] args) {
            //方式一
    //        MyRunnable my = new MyRunnable();
    //        Thread t = new Thread(my);
    //        t.start();
    
            //方式二
    //        new Thread(new Runnable() {
    //            @Override
    //            public void run() {
    //                System.out.println("多线程程序启动了");
    //            }
    //        }).start();
    
            //方式三
            new Thread( () -> {
                System.out.println("多线程程序启动了");
            } ).start();
        }
    }
    
  • 函数式编程思想概述

    函数式思想则尽量忽略面向对象的复杂语法:“强调做什么,而不是以什么形式去做”

    而我们要学习的Lambda表达式就是函数式思想的体现

1.2Lambda表达式的标准格式【理解】

  • 格式:

    (形式参数) -> {代码块}

    • 形式参数:如果有多个参数,参数之间用逗号隔开;如果没有参数,留空即可

    • ->:由英文中画线和大于符号组成,固定写法。代表指向动作

    • 代码块:是我们具体要做的事情,也就是以前我们写的方法体内容

  • 组成Lambda表达式的三要素:

    • 形式参数,箭头,代码块

1.3Lambda表达式练习1【应用】

  • Lambda表达式的使用前提

    • 有一个接口

    • 接口中有且仅有一个抽象方法

  • 练习描述

    无参无返回值抽象方法的练习

  • 操作步骤

    • 定义一个接口(Eatable),里面定义一个抽象方法:void eat();

    • 定义一个测试类(EatableDemo),在测试类中提供两个方法

      • 一个方法是:useEatable(Eatable e)

      • 一个方法是主方法,在主方法中调用useEatable方法

  • 示例代码

    //接口
    public interface Eatable {
        void eat();
    }
    //实现类
    public class EatableImpl implements Eatable {
        @Override
        public void eat() {
            System.out.println("一天一苹果,医生远离我");
        }
    }
    //测试类
    public class EatableDemo {
        public static void main(String[] args) {
            //在主方法中调用useEatable方法
            Eatable e = new EatableImpl();
            useEatable(e);
    
            //匿名内部类
            useEatable(new Eatable() {
                @Override
                public void eat() {
                    System.out.println("一天一苹果,医生远离我");
                }
            });
    
            //Lambda表达式
            useEatable(() -> {
                System.out.println("一天一苹果,医生远离我");
            });
        }
    
        private static void useEatable(Eatable e) {
            e.eat();
        }
    }
    

1.4Lambda表达式练习2【应用】

  • 练习描述

    有参无返回值抽象方法的练习

  • 操作步骤

    • 定义一个接口(Flyable),里面定义一个抽象方法:void fly(String s);

    • 定义一个测试类(FlyableDemo),在测试类中提供两个方法

      • 一个方法是:useFlyable(Flyable f)

      • 一个方法是主方法,在主方法中调用useFlyable方法

  • 示例代码

    public interface Flyable {
        void fly(String s);
    }
    
    public class FlyableDemo {
        public static void main(String[] args) {
            //在主方法中调用useFlyable方法
            //匿名内部类
            useFlyable(new Flyable() {
                @Override
                public void fly(String s) {
                    System.out.println(s);
                    System.out.println("飞机自驾游");
                }
            });
            System.out.println("--------");
    
            //Lambda
            useFlyable((String s) -> {
                System.out.println(s);
                System.out.println("飞机自驾游");
            });
    
        }
    
        private static void useFlyable(Flyable f) {
            f.fly("风和日丽,晴空万里");
        }
    }
    

1.5Lambda表达式练习3【应用】

  • 练习描述

    有参有返回值抽象方法的练习

  • 操作步骤

    • 定义一个接口(Addable),里面定义一个抽象方法:int add(int x,int y);

    • 定义一个测试类(AddableDemo),在测试类中提供两个方法

      • 一个方法是:useAddable(Addable a)

      • 一个方法是主方法,在主方法中调用useAddable方法

  • 示例代码

    public interface Addable {
        int add(int x,int y);
    }
    
    public class AddableDemo {
        public static void main(String[] args) {
            //在主方法中调用useAddable方法
            useAddable((int x,int y) -> {
                return x + y;
            });
    
        }
    
        private static void useAddable(Addable a) {
            int sum = a.add(10, 20);
            System.out.println(sum);
        }
    }
    

1.6Lambda表达式的省略模式【应用】

  • 省略的规则

    • 参数类型可以省略。但是有多个参数的情况下,不能只省略一个
    • 如果参数有且仅有一个,那么小括号可以省略
    • 如果代码块的语句只有一条,可以省略大括号和分号,和return关键字
  • 代码演示

    public interface Addable {
        int add(int x, int y);
    }
    
    public interface Flyable {
        void fly(String s);
    }
    
    public class LambdaDemo {
        public static void main(String[] args) {
    //        useAddable((int x,int y) -> {
    //            return x + y;
    //        });
            //参数的类型可以省略
            useAddable((x, y) -> {
                return x + y;
            });
    
    //        useFlyable((String s) -> {
    //            System.out.println(s);
    //        });
            //如果参数有且仅有一个,那么小括号可以省略
    //        useFlyable(s -> {
    //            System.out.println(s);
    //        });
    
            //如果代码块的语句只有一条,可以省略大括号和分号
            useFlyable(s -> System.out.println(s));
    
            //如果代码块的语句只有一条,可以省略大括号和分号,如果有return,return也要省略掉
            useAddable((x, y) -> x + y);
        }
    
        private static void useFlyable(Flyable f) {
            f.fly("风和日丽,晴空万里");
        }
    
        private static void useAddable(Addable a) {
            int sum = a.add(10, 20);
            System.out.println(sum);
        }
    }
    

1.7Lambda表达式的注意事项【理解】

  • 使用Lambda必须要有接口,并且要求接口中有且仅有一个抽象方法

  • 必须有上下文环境,才能推导出Lambda对应的接口

    • 根据局部变量的赋值得知Lambda对应的接口

      Runnable r = () -> System.out.println(“Lambda表达式”);

    • 根据调用方法的参数得知Lambda对应的接口

      new Thread(() -> System.out.println(“Lambda表达式”)).start();

1.8Lambda表达式和匿名内部类的区别【理解】

  • 所需类型不同

    • 匿名内部类:可以是接口,也可以是抽象类,还可以是具体类
    • Lambda表达式:只能是接口
  • 使用限制不同

    • 如果接口中有且仅有一个抽象方法,可以使用Lambda表达式,也可以使用匿名内部类

    • 如果接口中多于一个抽象方法,只能使用匿名内部类,而不能使用Lambda表达式

  • 实现原理不同

    • 匿名内部类:编译之后,产生一个单独的.class字节码文件
    • Lambda表达式:编译之后,没有一个单独的.class字节码文件。对应的字节码会在运行的时候动态生成

2.接口组成更新

2.1接口组成更新概述【理解】

  • 常量

    public static final

  • 抽象方法

    public abstract

  • 默认方法(Java 8)

  • 静态方法(Java 8)

  • 私有方法(Java 9)

2.2接口中默认方法【应用】

  • 格式

    public default 返回值类型 方法名(参数列表) { }

  • 范例

    public default void show3() { 
    }
    
  • 注意事项

    • 默认方法不是抽象方法,所以不强制被重写。但是可以被重写,重写的时候去掉default关键字

    • public可以省略,default不能省略

2.3接口中静态方法【应用】

  • 格式

    public static 返回值类型 方法名(参数列表) { }

  • 范例

    public static void show() {
    }
    
  • 注意事项

    • 静态方法只能通过接口名调用,不能通过实现类名或者对象名调用

    • public可以省略,static不能省略

2.4接口中私有方法【应用】

  • 私有方法产生原因

    Java 9中新增了带方法体的私有方法,这其实在Java 8中就埋下了伏笔:Java 8允许在接口中定义带方法体的默认方法和静态方法。这样可能就会引发一个问题:当两个默认方法或者静态方法中包含一段相同的代码实现时,程序必然考虑将这段实现代码抽取成一个共性方法,而这个共性方法是不需要让别人使用的,因此用私有给隐藏起来,这就是Java 9增加私有方法的必然性

  • 定义格式

    • 格式1

      private 返回值类型 方法名(参数列表) { }

    • 范例1

      private void show() {  
      }
      
    • 格式2

      private static 返回值类型 方法名(参数列表) { }

    • 范例2

      private static void method() {  
      }
      
  • 注意事项

    • 默认方法可以调用私有的静态方法和非静态方法
    • 静态方法只能调用私有的静态方法

3.方法引用

3.1体验方法引用【理解】

  • 方法引用的出现原因

    在使用Lambda表达式的时候,我们实际上传递进去的代码就是一种解决方案:拿参数做操作

    那么考虑一种情况:如果我们在Lambda中所指定的操作方案,已经有地方存在相同方案,那是否还有必要再写重复逻辑呢?答案肯定是没有必要

    那我们又是如何使用已经存在的方案的呢?

    这就是我们要讲解的方法引用,我们是通过方法引用来使用已经存在的方案

  • 代码演示

    public interface Printable {
        void printString(String s);
    }
    
    public class PrintableDemo {
        public static void main(String[] args) {
            //在主方法中调用usePrintable方法
    //        usePrintable((String s) -> {
    //            System.out.println(s);
    //        });
    	    //Lambda简化写法
            usePrintable(s -> System.out.println(s));
    
            //方法引用
            usePrintable(System.out::println);
    
        }
    
        private static void usePrintable(Printable p) {
            p.printString("爱生活爱Java");
        }
    }
    
    

3.2方法引用符【理解】

  • 方法引用符

    :: 该符号为引用运算符,而它所在的表达式被称为方法引用

  • 推导与省略

    • 如果使用Lambda,那么根据“可推导就是可省略”的原则,无需指定参数类型,也无需指定的重载形式,它们都将被自动推导
    • 如果使用方法引用,也是同样可以根据上下文进行推导
    • 方法引用是Lambda的孪生兄弟

3.3引用类方法【应用】

引用类方法,其实就是引用类的静态方法
  • 格式

    类名::静态方法

  • 范例

    Integer::parseInt

    Integer类的方法:public static int parseInt(String s) 将此String转换为int类型数据

  • 练习描述

    • 定义一个接口(Converter),里面定义一个抽象方法 int convert(String s);

    • 定义一个测试类(ConverterDemo),在测试类中提供两个方法

      • 一个方法是:useConverter(Converter c)

      • 一个方法是主方法,在主方法中调用useConverter方法

  • 代码演示

    public interface Converter {
        int convert(String s);
    }
    
    public class ConverterDemo {
        public static void main(String[] args) {
    
    		//Lambda写法
            useConverter(s -> Integer.parseInt(s));
    
            //引用类方法
            useConverter(Integer::parseInt);
    
        }
    
        private static void useConverter(Converter c) {
            int number = c.convert("666");
            System.out.println(number);
        }
    }
    
  • 使用说明

    Lambda表达式被类方法替代的时候,它的形式参数全部传递给静态方法作为参数

3.4引用对象的实例方法【应用】

引用对象的实例方法,其实就引用类中的成员方法
  • 格式

    对象::成员方法

  • 范例

    “HelloWorld”::toUpperCase

    String类中的方法:public String toUpperCase() 将此String所有字符转换为大写

  • 练习描述

    • 定义一个类(PrintString),里面定义一个方法

      public void printUpper(String s):把字符串参数变成大写的数据,然后在控制台输出

    • 定义一个接口(Printer),里面定义一个抽象方法

      void printUpperCase(String s)

    • 定义一个测试类(PrinterDemo),在测试类中提供两个方法

      • 一个方法是:usePrinter(Printer p)
      • 一个方法是主方法,在主方法中调用usePrinter方法
  • 代码演示

    public class PrintString {
        //把字符串参数变成大写的数据,然后在控制台输出
        public void printUpper(String s) {
            String result = s.toUpperCase();
            System.out.println(result);
        }
    }
    
    public interface Printer {
        void printUpperCase(String s);
    }
    
    public class PrinterDemo {
        public static void main(String[] args) {
    
    		//Lambda简化写法
            usePrinter(s -> System.out.println(s.toUpperCase()));
    
            //引用对象的实例方法
            PrintString ps = new PrintString();
            usePrinter(ps::printUpper);
    
        }
    
        private static void usePrinter(Printer p) {
            p.printUpperCase("HelloWorld");
        }
    }
    
    
  • 使用说明

    Lambda表达式被对象的实例方法替代的时候,它的形式参数全部传递给该方法作为参数

3.5引用类的实例方法【应用】

引用类的实例方法,其实就是引用类中的成员方法
  • 格式

    类名::成员方法

  • 范例

    String::substring

    public String substring(int beginIndex,int endIndex)

    从beginIndex开始到endIndex结束,截取字符串。返回一个子串,子串的长度为endIndex-beginIndex

  • 练习描述

    • 定义一个接口(MyString),里面定义一个抽象方法:

      String mySubString(String s,int x,int y);

    • 定义一个测试类(MyStringDemo),在测试类中提供两个方法

      • 一个方法是:useMyString(MyString my)

      • 一个方法是主方法,在主方法中调用useMyString方法

  • 代码演示

    public interface MyString {
        String mySubString(String s,int x,int y);
    }
    
    public class MyStringDemo {
        public static void main(String[] args) {
    		//Lambda简化写法
            useMyString((s,x,y) -> s.substring(x,y));
    
            //引用类的实例方法
            useMyString(String::substring);
    
        }
    
        private static void useMyString(MyString my) {
            String s = my.mySubString("HelloWorld", 2, 5);
            System.out.println(s);
        }
    }
    
  • 使用说明

    Lambda表达式被类的实例方法替代的时候
    第一个参数作为调用者
    后面的参数全部传递给该方法作为参数
    

3.6引用构造器【应用】

引用构造器,其实就是引用构造方法
  • l格式

    类名::new

  • 范例

    Student::new

  • 练习描述

    • 定义一个类(Student),里面有两个成员变量(name,age)

      并提供无参构造方法和带参构造方法,以及成员变量对应的get和set方法

    • 定义一个接口(StudentBuilder),里面定义一个抽象方法

      Student build(String name,int age);

    • 定义一个测试类(StudentDemo),在测试类中提供两个方法

      • 一个方法是:useStudentBuilder(StudentBuilder s)

      • 一个方法是主方法,在主方法中调用useStudentBuilder方法

  • 代码演示

    public class Student {
        private String name;
        private int age;
    
        public Student() {
        }
    
        public Student(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;
        }
    }
    
    public interface StudentBuilder {
        Student build(String name,int age);
    }
    
    public class StudentDemo {
        public static void main(String[] args) {
    
    		//Lambda简化写法
            useStudentBuilder((name,age) -> new Student(name,age));
    
            //引用构造器
            useStudentBuilder(Student::new);
    
        }
    
        private static void useStudentBuilder(StudentBuilder sb) {
            Student s = sb.build("林青霞", 30);
            System.out.println(s.getName() + "," + s.getAge());
        }
    }
    
  • 使用说明

    Lambda表达式被构造器替代的时候,它的形式参数全部传递给构造器作为参数

4.函数式接口

4.1函数式接口概述【理解】

  • 概念

    有且仅有一个抽象方法的接口

  • 如何检测一个接口是不是函数式接口

    @FunctionalInterface

    放在接口定义的上方:如果接口是函数式接口,编译通过;如果不是,编译失败

  • 注意事项

    我们自己定义函数式接口的时候,@FunctionalInterface是可选的,就算我不写这个注解,只要保证满足函数式接口定义的条件,也照样是函数式接口。但是,建议加上该注解

4.2函数式接口作为方法的参数【应用】

  • 需求描述

    定义一个类(RunnableDemo),在类中提供两个方法

    一个方法是:startThread(Runnable r) 方法参数Runnable是一个函数式接口

    一个方法是主方法,在主方法中调用startThread方法

  • 代码演示

    public class RunnableDemo {
        public static void main(String[] args) {
            //在主方法中调用startThread方法
    
            //匿名内部类的方式
            startThread(new Runnable() {
                @Override
                public void run() {
                    System.out.println(Thread.currentThread().getName() + "线程启动了");
                }
            });
            
    		//Lambda方式
            startThread(() -> System.out.println(Thread.currentThread().getName() + "线程启动了"));
    
        }
    
        private static void startThread(Runnable r) {
            new Thread(r).start();
        }
    }
    

4.3函数式接口作为方法的返回值【应用】

  • 需求描述

    定义一个类(ComparatorDemo),在类中提供两个方法

    一个方法是:Comparator getComparator() 方法返回值Comparator是一个函数式接口

    一个方法是主方法,在主方法中调用getComparator方法

  • 代码演示

    public class ComparatorDemo {
        public static void main(String[] args) {
            //定义集合,存储字符串元素
            ArrayList<String> array = new ArrayList<String>();
    
            array.add("cccc");
            array.add("aa");
            array.add("b");
            array.add("ddd");
    
            System.out.println("排序前:" + array);
    
            Collections.sort(array, getComparator());
    
            System.out.println("排序后:" + array);
    
        }
    
        private static Comparator<String> getComparator() {
            //匿名内部类的方式实现
    //        return new Comparator<String>() {
    //            @Override
    //            public int compare(String s1, String s2) {
    //                return s1.length()-s2.length();
    //            }
    //        };
            
    		//Lambda方式实现
            return (s1, s2) -> s1.length() - s2.length();
        }
    }
    

4.4常用函数式接口之Supplier【应用】

  • Supplier接口

    Supplier接口也被称为生产型接口,如果我们指定了接口的泛型是什么类型,那么接口中的get方法就会生产什么类型的数据供我们使用。

  • 常用方法

    只有一个无参的方法

    方法名说明
    T get()按照某种实现逻辑(由Lambda表达式实现)返回一个数据
  • 代码演示

    public class SupplierDemo {
        public static void main(String[] args) {
    
            String s = getString(() -> "林青霞");
            System.out.println(s);
            
            Integer i = getInteger(() -> 30);
            System.out.println(i);
        }
    
        //定义一个方法,返回一个整数数据
        private static Integer getInteger(Supplier<Integer> sup) {
            return sup.get();
        }
    
        //定义一个方法,返回一个字符串数据
        private static String getString(Supplier<String> sup) {
            return sup.get();
        }
    
    }
    

4.5Supplier接口练习之获取最大值【应用】

  • 案例需求

    定义一个类(SupplierTest),在类中提供两个方法

    一个方法是:int getMax(Supplier sup) 用于返回一个int数组中的最大值

    一个方法是主方法,在主方法中调用getMax方法

  • 示例代码

    public class SupplierTest {
        public static void main(String[] args) {
            //定义一个int数组
            int[] arr = {19, 50, 28, 37, 46};
    
            int maxValue = getMax(()-> {
               int max = arr[0];
    
               for(int i=1; i<arr.length; i++) {
                   if(arr[i] > max) {
                       max = arr[i];
                   }
               }
    
               return max;
            });
    
            System.out.println(maxValue);
    
        }
    
        //返回一个int数组中的最大值
        private static int getMax(Supplier<Integer> sup) {
            return sup.get();
        }
    }
    

4.6常用函数式接口之Consumer【应用】

  • Consumer接口

    Consumer接口也被称为消费型接口,它消费的数据的数据类型由泛型指定

  • 常用方法

    Consumer:包含两个方法

    方法名说明
    void accept(T t)对给定的参数执行此操作
    default Consumer andThen(Consumer after)返回一个组合的Consumer,依次执行此操作,然后执行 after操作
  • 代码演示

    public class ConsumerDemo {
        public static void main(String[] args) {
    		//操作一
            operatorString("林青霞", s -> System.out.println(s));
    		//操作二
            operatorString("林青霞", s -> System.out.println(new StringBuilder(s).reverse().toString()));
            
            System.out.println("--------");
    		//传入两个操作使用andThen完成
            operatorString("林青霞", s -> System.out.println(s), s -> System.out.println(new StringBuilder(s).reverse().toString()));
        }
    
        //定义一个方法,用不同的方式消费同一个字符串数据两次
        private static void operatorString(String name, Consumer<String> con1, Consumer<String> con2) {
    //        con1.accept(name);
    //        con2.accept(name);
            con1.andThen(con2).accept(name);
        }
    
        //定义一个方法,消费一个字符串数据
        private static void operatorString(String name, Consumer<String> con) {
            con.accept(name);
        }
    }
    

4.7Consumer接口练习之按要求打印信息【应用】

  • 案例需求

    String[] strArray = {“林青霞,30”, “张曼玉,35”, “王祖贤,33”};

    字符串数组中有多条信息,请按照格式:“姓名:XX,年龄:XX"的格式将信息打印出来

    要求:

    把打印姓名的动作作为第一个Consumer接口的Lambda实例

    把打印年龄的动作作为第二个Consumer接口的Lambda实例

    将两个Consumer接口按照顺序组合到一起使用

  • 示例代码

    public class ConsumerTest {
        public static void main(String[] args) {
            String[] strArray = {"林青霞,30", "张曼玉,35", "王祖贤,33"};
    
            printInfo(strArray, str -> System.out.print("姓名:" + str.split(",")[0]),
                    str -> System.out.println(",年龄:" + Integer.parseInt(str.split(",")[1])));
        }
    
        private static void printInfo(String[] strArray, Consumer<String> con1, Consumer<String> con2) {
            for (String str : strArray) {
                con1.andThen(con2).accept(str);
            }
        }
    }
    

4.8常用函数式接口之Predicate【应用】

  • Predicate接口

    Predicate接口通常用于判断参数是否满足指定的条件

  • 常用方法

    方法名说明
    boolean test(T t)对给定的参数进行判断(判断逻辑由Lambda表达式实现),返回一个布尔值
    default Predicate negate()返回一个逻辑的否定,对应逻辑非
    default Predicate and(Predicate other)返回一个组合判断,对应短路与
    default Predicate or(Predicate other)返回一个组合判断,对应短路或
  • 代码演示

    public class PredicateDemo01 {
        public static void main(String[] args) {
            boolean b1 = checkString("hello", s -> s.length() > 8);
            System.out.println(b1);
    
            boolean b2 = checkString("helloworld",s -> s.length() > 8);
            System.out.println(b2);
    
        }
    
        //判断给定的字符串是否满足要求
        private static boolean checkString(String s, Predicate<String> pre) {
    //        return !pre.test(s);
            return pre.negate().test(s);
        }
    }
    
    public class PredicateDemo02 {
        public static void main(String[] args) {
            boolean b1 = checkString("hello", s -> s.length() > 8);
            System.out.println(b1);
            boolean b2 = checkString("helloworld", s -> s.length() > 8);
            System.out.println(b2);
    
            boolean b3 = checkString("hello",s -> s.length() > 8, s -> s.length() < 15);
            System.out.println(b3);
    
            boolean b4 = checkString("helloworld",s -> s.length() > 8, s -> s.length() < 15);
            System.out.println(b4);
        }
    
        //同一个字符串给出两个不同的判断条件,最后把这两个判断的结果做逻辑与运算的结果作为最终的结果
        private static boolean checkString(String s, Predicate<String> pre1, Predicate<String> pre2) {
            return pre1.or(pre2).test(s);
        }
    
        //判断给定的字符串是否满足要求
        private static boolean checkString(String s, Predicate<String> pre) {
            return pre.test(s);
        }
    }
    

4.9Predicate接口练习之筛选满足条件数据【应用】

  • 练习描述

    • String[] strArray = {“林青霞,30”, “柳岩,34”, “张曼玉,35”, “貂蝉,31”, “王祖贤,33”};

    • 字符串数组中有多条信息,请通过Predicate接口的拼装将符合要求的字符串筛选到集合ArrayList中,并遍历ArrayList集合

    • 同时满足如下要求:姓名长度大于2;年龄大于33

  • 分析

    • 有两个判断条件,所以需要使用两个Predicate接口,对条件进行判断

    • 必须同时满足两个条件,所以可以使用and方法连接两个判断条件

  • 示例代码

    public class PredicateTest {
        public static void main(String[] args) {
            String[] strArray = {"林青霞,30", "柳岩,34", "张曼玉,35", "貂蝉,31", "王祖贤,33"};
    
            ArrayList<String> array = myFilter(strArray, s -> s.split(",")[0].length() > 2,
                    s -> Integer.parseInt(s.split(",")[1]) > 33);
    
            for (String str : array) {
                System.out.println(str);
            }
        }
    
        //通过Predicate接口的拼装将符合要求的字符串筛选到集合ArrayList中
        private static ArrayList<String> myFilter(String[] strArray, Predicate<String> pre1, Predicate<String> pre2) {
            //定义一个集合
            ArrayList<String> array = new ArrayList<String>();
    
            //遍历数组
            for (String str : strArray) {
                if (pre1.and(pre2).test(str)) {
                    array.add(str);
                }
            }
    
            return array;
        }
    }
    

4.10常用函数式接口之Function【应用】

  • Function接口

    Function<T,R>接口通常用于对参数进行处理,转换(处理逻辑由Lambda表达式实现),然后返回一个新的值

  • 常用方法

    方法名说明
    R apply(T t)将此函数应用于给定的参数
    default Function andThen(Function after)返回一个组合函数,首先将该函数应用于输入,然后将after函数应用于结果
  • 代码演示

    public class FunctionDemo {
        public static void main(String[] args) {
    		//操作一
            convert("100",s -> Integer.parseInt(s));
    		//操作二
            convert(100,i -> String.valueOf(i + 566));
    		
            //使用andThen的方式连续执行两个操作
            convert("100", s -> Integer.parseInt(s), i -> String.valueOf(i + 566));
        }
    
        //定义一个方法,把一个字符串转换int类型,在控制台输出
        private static void convert(String s, Function<String,Integer> fun) {
    //        Integer i = fun.apply(s);
            int i = fun.apply(s);
            System.out.println(i);
        }
    
    
        //定义一个方法,把一个int类型的数据加上一个整数之后,转为字符串在控制台输出
        private static void convert(int i, Function<Integer,String> fun) {
            String s = fun.apply(i);
            System.out.println(s);
        }
    
    
        //定义一个方法,把一个字符串转换int类型,把int类型的数据加上一个整数之后,转为字符串在控制台输出
        private static void convert(String s, Function<String,Integer> fun1, Function<Integer,String> fun2) {
    
            String ss = fun1.andThen(fun2).apply(s);
            System.out.println(ss);
        }
    
    }
    

4.11Function接口练习之按照指定要求操作数据【应用】

  • 练习描述

    • String s = “林青霞,30”;

    • 请按照我指定的要求进行操作:

      1:将字符串截取得到数字年龄部分

      2:将上一步的年龄字符串转换成为int类型的数据

      3:将上一步的int数据加70,得到一个int结果,在控制台输出

    • 请通过Function接口来实现函数拼接

  • 示例代码

    public class FunctionTest {
        public static void main(String[] args) {
            String s = "林青霞,30";
            convert(s, ss -> ss.split(",")[1], Integer::parseInt, i -> i + 70);
        }
    
        private static void convert(String s, Function<String, String> fun1, Function<String, Integer> fun2, Function<Integer, Integer> fun3) {
            int i = fun1.andThen(fun2).andThen(fun3).apply(s);
            System.out.println(i);
        }
    }
    

5.Strem流

5.1体验Stream流【理解】

  • 案例需求

    按照下面的要求完成集合的创建和遍历

    • 创建一个集合,存储多个字符串元素
    • 把集合中所有以"张"开头的元素存储到一个新的集合
    • 把"张"开头的集合中的长度为3的元素存储到一个新的集合
    • 遍历上一步得到的集合
  • 原始方式示例代码

    public class StreamDemo {
        public static void main(String[] args) {
            //创建一个集合,存储多个字符串元素
            ArrayList<String> list = new ArrayList<String>();
    
            list.add("林青霞");
            list.add("张曼玉");
            list.add("王祖贤");
            list.add("柳岩");
            list.add("张敏");
            list.add("张无忌");
    
            //把集合中所有以"张"开头的元素存储到一个新的集合
            ArrayList<String> zhangList = new ArrayList<String>();
    
            for(String s : list) {
                if(s.startsWith("张")) {
                    zhangList.add(s);
                }
            }
    
    //        System.out.println(zhangList);
    
            //把"张"开头的集合中的长度为3的元素存储到一个新的集合
            ArrayList<String> threeList = new ArrayList<String>();
    
            for(String s : zhangList) {
                if(s.length() == 3) {
                    threeList.add(s);
                }
            }
    
    //        System.out.println(threeList);
    
            //遍历上一步得到的集合
            for(String s : threeList) {
                System.out.println(s);
            }
            System.out.println("--------");
    
            //Stream流来改进
    //        list.stream().filter(s -> s.startsWith("张")).filter(s -> s.length() == 3).forEach(s -> System.out.println(s));
            list.stream().filter(s -> s.startsWith("张")).filter(s -> s.length() == 3).forEach(System.out::println);
        }
    }
    
  • 使用Stream流示例代码

    public class StreamDemo {
        public static void main(String[] args) {
            //创建一个集合,存储多个字符串元素
            ArrayList<String> list = new ArrayList<String>();
    
            list.add("林青霞");
            list.add("张曼玉");
            list.add("王祖贤");
            list.add("柳岩");
            list.add("张敏");
            list.add("张无忌");
    
            //Stream流来改进
            list.stream().filter(s -> s.startsWith("张")).filter(s -> s.length() == 3).forEach(System.out::println);
        }
    }
    
  • Stream流的好处

    • 直接阅读代码的字面意思即可完美展示无关逻辑方式的语义:获取流、过滤姓张、过滤长度为3、逐一打印

    • Stream流把真正的函数式编程风格引入到Java中

5.2Stream流的常见生成方式【应用】

  • Stream流的思想

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GV6qeQsu-1594954235901)(img\Stream流思想.jpg)]

  • 生成Stream流的方式

    • Collection体系集合

      使用默认方法stream()生成流, default Stream stream()

    • Map体系集合

      把Map转成Set集合,间接的生成流

    • 数组

      通过Stream接口的静态方法of(T… values)生成流

  • 代码演示

    public class StreamDemo {
        public static void main(String[] args) {
            //Collection体系的集合可以使用默认方法stream()生成流
            List<String> list = new ArrayList<String>();
            Stream<String> listStream = list.stream();
    
            Set<String> set = new HashSet<String>();
            Stream<String> setStream = set.stream();
    
            //Map体系的集合间接的生成流
            Map<String,Integer> map = new HashMap<String, Integer>();
            Stream<String> keyStream = map.keySet().stream();
            Stream<Integer> valueStream = map.values().stream();
            Stream<Map.Entry<String, Integer>> entryStream = map.entrySet().stream();
    
            //数组可以通过Stream接口的静态方法of(T... values)生成流
            String[] strArray = {"hello","world","java"};
            Stream<String> strArrayStream = Stream.of(strArray);
            Stream<String> strArrayStream2 = Stream.of("hello", "world", "java");
            Stream<Integer> intStream = Stream.of(10, 20, 30);
        }
    }
    

5.3Stream流中间操作方法【应用】

  • 概念

    中间操作的意思是,执行完此方法之后,Stream流依然可以继续执行其他操作。

  • 常见方法

    方法名说明
    Stream filter(Predicate predicate)用于对流中的数据进行过滤
    Stream limit(long maxSize)返回此流中的元素组成的流,截取前指定参数个数的数据
    Stream skip(long n)跳过指定参数个数的数据,返回由该流的剩余元素组成的流
    static Stream concat(Stream a, Stream b)合并a和b两个流为一个流
    Stream distinct()返回由该流的不同元素(根据Object.equals(Object) )组成的流
    Stream sorted()返回由此流的元素组成的流,根据自然顺序排序
    Stream sorted(Comparator comparator)返回由该流的元素组成的流,根据提供的Comparator进行排序
    Stream map(Function mapper)返回由给定函数应用于此流的元素的结果组成的流
    IntStream mapToInt(ToIntFunction mapper)返回一个IntStream其中包含将给定函数应用于此流的元素的结果
  • filter代码演示

    public class StreamDemo01 {
        public static void main(String[] args) {
            //创建一个集合,存储多个字符串元素
            ArrayList<String> list = new ArrayList<String>();
    
            list.add("林青霞");
            list.add("张曼玉");
            list.add("王祖贤");
            list.add("柳岩");
            list.add("张敏");
            list.add("张无忌");
    
            //需求1:把list集合中以张开头的元素在控制台输出
            list.stream().filter(s -> s.startsWith("张")).forEach(System.out::println);
            System.out.println("--------");
    
            //需求2:把list集合中长度为3的元素在控制台输出
            list.stream().filter(s -> s.length() == 3).forEach(System.out::println);
            System.out.println("--------");
    
            //需求3:把list集合中以张开头的,长度为3的元素在控制台输出
            list.stream().filter(s -> s.startsWith("张")).filter(s -> s.length() == 3).forEach(System.out::println);
        }
    }
    
  • limit&skip代码演示

    public class StreamDemo02 {
        public static void main(String[] args) {
            //创建一个集合,存储多个字符串元素
            ArrayList<String> list = new ArrayList<String>();
    
            list.add("林青霞");
            list.add("张曼玉");
            list.add("王祖贤");
            list.add("柳岩");
            list.add("张敏");
            list.add("张无忌");
    
            //需求1:取前3个数据在控制台输出
            list.stream().limit(3).forEach(System.out::println);
            System.out.println("--------");
    
            //需求2:跳过3个元素,把剩下的元素在控制台输出
            list.stream().skip(3).forEach(System.out::println);
            System.out.println("--------");
    
            //需求3:跳过2个元素,把剩下的元素中前2个在控制台输出
            list.stream().skip(2).limit(2).forEach(System.out::println);
        }
    }
    
  • concat&distinct代码演示

    public class StreamDemo03 {
        public static void main(String[] args) {
            //创建一个集合,存储多个字符串元素
            ArrayList<String> list = new ArrayList<String>();
    
            list.add("林青霞");
            list.add("张曼玉");
            list.add("王祖贤");
            list.add("柳岩");
            list.add("张敏");
            list.add("张无忌");
    
            //需求1:取前4个数据组成一个流
            Stream<String> s1 = list.stream().limit(4);
    
            //需求2:跳过2个数据组成一个流
            Stream<String> s2 = list.stream().skip(2);
    
            //需求3:合并需求1和需求2得到的流,并把结果在控制台输出
    //        Stream.concat(s1,s2).forEach(System.out::println);
    
            //需求4:合并需求1和需求2得到的流,并把结果在控制台输出,要求字符串元素不能重复
            Stream.concat(s1,s2).distinct().forEach(System.out::println);
        }
    }
    
  • sorted代码演示

    public class StreamDemo04 {
        public static void main(String[] args) {
            //创建一个集合,存储多个字符串元素
            ArrayList<String> list = new ArrayList<String>();
    
            list.add("linqingxia");
            list.add("zhangmanyu");
            list.add("wangzuxian");
            list.add("liuyan");
            list.add("zhangmin");
            list.add("zhangwuji");
    
            //需求1:按照字母顺序把数据在控制台输出
    //        list.stream().sorted().forEach(System.out::println);
    
            //需求2:按照字符串长度把数据在控制台输出
            list.stream().sorted((s1,s2) -> {
                int num = s1.length()-s2.length();
                int num2 = num==0?s1.compareTo(s2):num;
                return num2;
            }).forEach(System.out::println);
        }
    }
    
  • map&mapToInt代码演示

    public class StreamDemo05 {
        public static void main(String[] args) {
            //创建一个集合,存储多个字符串元素
            ArrayList<String> list = new ArrayList<String>();
    
            list.add("10");
            list.add("20");
            list.add("30");
            list.add("40");
            list.add("50");
    
            //需求:将集合中的字符串数据转换为整数之后在控制台输出
    //        list.stream().map(s -> Integer.parseInt(s)).forEach(System.out::println);
    //        list.stream().map(Integer::parseInt).forEach(System.out::println);
    //        list.stream().mapToInt(Integer::parseInt).forEach(System.out::println);
    
            //int sum() 返回此流中元素的总和
            int result = list.stream().mapToInt(Integer::parseInt).sum();
            System.out.println(result);
        }
    }
    

5.4Stream流终结操作方法【应用】

  • 概念

    终结操作的意思是,执行完此方法之后,Stream流将不能再执行其他操作。

  • 常见方法

    方法名说明
    void forEach(Consumer action)对此流的每个元素执行操作
    long count()返回此流中的元素数
  • 代码演示

    public class StreamDemo {
        public static void main(String[] args) {
            //创建一个集合,存储多个字符串元素
            ArrayList<String> list = new ArrayList<String>();
    
            list.add("林青霞");
            list.add("张曼玉");
            list.add("王祖贤");
            list.add("柳岩");
            list.add("张敏");
            list.add("张无忌");
    
            //需求1:把集合中的元素在控制台输出
    //        list.stream().forEach(System.out::println);
    
            //需求2:统计集合中有几个以张开头的元素,并把统计结果在控制台输出
            long count = list.stream().filter(s -> s.startsWith("张")).count();
            System.out.println(count);
        }
    }
    

5.5Stream流综合练习【应用】

  • 案例需求

    现在有两个ArrayList集合,分别存储6名男演员名称和6名女演员名称,要求完成如下的操作

    • 男演员只要名字为3个字的前三人

    • 女演员只要姓林的,并且不要第一个

    • 把过滤后的男演员姓名和女演员姓名合并到一起

    • 把上一步操作后的元素作为构造方法的参数创建演员对象,遍历数据

    演员类Actor已经提供,里面有一个成员变量,一个带参构造方法,以及成员变量对应的get/set方法

  • 代码实现

    public class Actor {
        private String name;
    
        public Actor(String name) {
            this.name = name;
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    }
    
    
    public class StreamTest {
        public static void main(String[] args) {
            //创建集合
            ArrayList<String> manList = new ArrayList<String>();
            manList.add("周润发");
            manList.add("成龙");
            manList.add("刘德华");
            manList.add("吴京");
            manList.add("周星驰");
            manList.add("李连杰");
    
    
            ArrayList<String> womanList = new ArrayList<String>();
            womanList.add("林心如");
            womanList.add("张曼玉");
            womanList.add("林青霞");
            womanList.add("柳岩");
            womanList.add("林志玲");
            womanList.add("王祖贤");
    
            /*
            //男演员只要名字为3个字的前三人
            Stream<String> manStream = manList.stream().filter(s -> s.length() == 3).limit(3);
    
            //女演员只要姓林的,并且不要第一个
            Stream<String> womanStream = womanList.stream().filter(s -> s.startsWith("林")).skip(1);
    
            //把过滤后的男演员姓名和女演员姓名合并到一起
            Stream<String> stream = Stream.concat(manStream, womanStream);
    
            //把上一步操作后的元素作为构造方法的参数创建演员对象,遍历数据
    //        stream.map(Actor::new).forEach(System.out::println);
            stream.map(Actor::new).forEach(p -> System.out.println(p.getName()));
            */
    
            Stream.concat(manList.stream().filter(s -> s.length() == 3).limit(3),
                    womanList.stream().filter(s -> s.startsWith("林")).skip(1)).map(Actor::new).
                    forEach(p -> System.out.println(p.getName()));
        }
    }
    

5.6Stream流的收集操作【应用】

  • 概念

    对数据使用Stream流的方式操作完毕后,可以把流中的数据收集到集合中。

  • 常用方法

    方法名说明
    R collect(Collector collector)把结果收集到集合中
  • 工具类Collectors提供了具体的收集方式

    方法名说明
    public static Collector toList()把元素收集到List集合中
    public static Collector toSet()把元素收集到Set集合中
    public static Collector toMap(Function keyMapper,Function valueMapper)把元素收集到Map集合中
  • 代码演示

    public class CollectDemo {
        public static void main(String[] args) {
            //创建List集合对象
            List<String> list = new ArrayList<String>();
            list.add("林青霞");
            list.add("张曼玉");
            list.add("王祖贤");
            list.add("柳岩");
    
            /*
            //需求1:得到名字为3个字的流
            Stream<String> listStream = list.stream().filter(s -> s.length() == 3);
    
            //需求2:把使用Stream流操作完毕的数据收集到List集合中并遍历
            List<String> names = listStream.collect(Collectors.toList());
            for(String name : names) {
                System.out.println(name);
            }
            */
    
            //创建Set集合对象
            Set<Integer> set = new HashSet<Integer>();
            set.add(10);
            set.add(20);
            set.add(30);
            set.add(33);
            set.add(35);
    
            /*
            //需求3:得到年龄大于25的流
            Stream<Integer> setStream = set.stream().filter(age -> age > 25);
    
            //需求4:把使用Stream流操作完毕的数据收集到Set集合中并遍历
            Set<Integer> ages = setStream.collect(Collectors.toSet());
            for(Integer age : ages) {
                System.out.println(age);
            }
            */
            //定义一个字符串数组,每一个字符串数据由姓名数据和年龄数据组合而成
            String[] strArray = {"林青霞,30", "张曼玉,35", "王祖贤,33", "柳岩,25"};
    
            //需求5:得到字符串中年龄数据大于28的流
            Stream<String> arrayStream = Stream.of(strArray).filter(s -> Integer.parseInt(s.split(",")[1]) > 28);
    
            //需求6:把使用Stream流操作完毕的数据收集到Map集合中并遍历,字符串中的姓名作键,年龄作值
            Map<String, Integer> map = arrayStream.collect(Collectors.toMap(s -> s.split(",")[0], s -> Integer.parseInt(s.split(",")[1])));
    
            Set<String> keySet = map.keySet();
            for (String key : keySet) {
                Integer value = map.get(key);
                System.out.println(key + "," + value);
            }
        }
    }
    

6.类加载器

6.1类加载【理解】

  • 类加载的描述
    • 当程序要使用某个类时,如果该类还未被加载到内存中,则系统会通过类的加载,类的连接,类的初始化这三个步骤来对类进行初始化。如果不出现意外情况,JVM将会连续完成这三个步骤,所以有时也把这三个步骤统称为类加载或者类初始化
  • 类的加载
    • 就是指将class文件读入内存,并为之创建一个 java.lang.Class 对象
    • 任何类被使用时,系统都会为之建立一个 java.lang.Class 对象
  • 类的连接
    • 验证阶段:用于检验被加载的类是否有正确的内部结构,并和其他类协调一致
    • 准备阶段:负责为类的类变量分配内存,并设置默认初始化值
    • 解析阶段:将类的二进制数据中的符号引用替换为直接引用
  • 类的初始化
    • 在该阶段,主要就是对类变量进行初始化
  • 类的初始化步骤
    • 假如类还未被加载和连接,则程序先加载并连接该类
    • 假如该类的直接父类还未被初始化,则先初始化其直接父类
    • 假如类中有初始化语句,则系统依次执行这些初始化语句
    • 注意:在执行第2个步骤的时候,系统对直接父类的初始化步骤也遵循初始化步骤1-3
  • 类的初始化时机
    • 创建类的实例
    • 调用类的类方法
    • 访问类或者接口的类变量,或者为该类变量赋值
    • 使用反射方式来强制创建某个类或接口对应的java.lang.Class对象
    • 初始化某个类的子类
    • 直接使用java.exe命令来运行某个主类

6.2类加载器【理解】

6.2.1类加载器的作用
  • 负责将.class文件加载到内存中,并为之生成对应的 java.lang.Class 对象。虽然我们不用过分关心类加载机制,但是了解这个机制我们就能更好的理解程序的运行!
6.2.2JVM的类加载机制
  • 全盘负责:就是当一个类加载器负责加载某个Class时,该Class所依赖的和引用的其他Class也将由该类加载器负责载入,除非显示使用另外一个类加载器来载入
  • 父类委托:就是当一个类加载器负责加载某个Class时,先让父类加载器试图加载该Class,只有在父类加载器无法加载该类时才尝试从自己的类路径中加载该类
  • 缓存机制:保证所有加载过的Class都会被缓存,当程序需要使用某个Class对象时,类加载器先从缓存区中搜索该Class,只有当缓存区中不存在该Class对象时,系统才会读取该类对应的二进制数据,并将其转换成Class对象,存储到缓存区
6.2.3Java中的内置类加载器
  • Bootstrap class loader:它是虚拟机的内置类加载器,通常表示为null ,并且没有父null
  • Platform class loader:平台类加载器可以看到所有平台类 ,平台类包括由平台类加载器或其祖先定义的Java SE平台API,其实现类和JDK特定的运行时类
  • System class loader:它也被称为应用程序类加载器 ,与平台类加载器不同。 系统类加载器通常用于定义应用程序类路径,模块路径和JDK特定工具上的类
  • 类加载器的继承关系:System的父加载器为Platform,而Platform的父加载器为Bootstrap
6.2.4ClassLoader 中的两个方法
  • 方法分类

    方法名说明
    static ClassLoader getSystemClassLoader()返回用于委派的系统类加载器
    ClassLoader getParent()返回父类加载器进行委派
  • 示例代码

    public class ClassLoaderDemo {
        public static void main(String[] args) {
            //static ClassLoader getSystemClassLoader():返回用于委派的系统类加载器
            ClassLoader c = ClassLoader.getSystemClassLoader();
            System.out.println(c); //AppClassLoader
    
            //ClassLoader getParent():返回父类加载器进行委派
            ClassLoader c2 = c.getParent();
            System.out.println(c2); //PlatformClassLoader
    
            ClassLoader c3 = c2.getParent();
            System.out.println(c3); //null
        }
    }
    

7.反射

7.1反射的概述【理解】

  • 是指在运行时去获取一个类的变量和方法信息。然后通过获取到的信息来创建对象,调用方法的一种机制。由于这种动态性,可以极大的增强程序的灵活性,程序不用在编译期就完成确定,在运行期仍然可以扩展

7.2获取Class类对象的三种方式【应用】

7.2.1三种方式分类
  • 类名.class属性
  • 对象名.getClass()方法
  • Class.forName(全类名)方法
7.2.2示例代码
public class ReflectDemo {
    public static void main(String[] args) throws ClassNotFoundException {
        //使用类的class属性来获取该类对应的Class对象
        Class<Student> c1 = Student.class;
        System.out.println(c1);

        Class<Student> c2 = Student.class;
        System.out.println(c1 == c2);
        System.out.println("--------");

        //调用对象的getClass()方法,返回该对象所属类对应的Class对象
        Student s = new Student();
        Class<? extends Student> c3 = s.getClass();
        System.out.println(c1 == c3);
        System.out.println("--------");

        //使用Class类中的静态方法forName(String className)
        Class<?> c4 = Class.forName("com.itheima_02.Student");
        System.out.println(c1 == c4);
    }
}

7.3反射获取构造方法并使用【应用】

7.3.1Class类获取构造方法对象的方法
  • 方法分类

    方法名说明
    Constructor<?>[] getConstructors()返回所有公共构造方法对象的数组
    Constructor<?>[] getDeclaredConstructors()返回所有构造方法对象的数组
    Constructor getConstructor(Class<?>… parameterTypes)返回单个公共构造方法对象
    Constructor getDeclaredConstructor(Class<?>… parameterTypes)返回单个构造方法对象
  • 示例代码

    public class ReflectDemo01 {
        public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
            //获取Class对象
            Class<?> c = Class.forName("com.itheima_02.Student");
    
            //Constructor<?>[] getConstructors() 返回一个包含 Constructor对象的数组, Constructor对象反映了由该 Class对象表示的类的所有公共构造函数
    //        Constructor<?>[] cons = c.getConstructors();
            //Constructor<?>[] getDeclaredConstructors() 返回反映由该 Class对象表示的类声明的所有构造函数的 Constructor对象的数组
            Constructor<?>[] cons = c.getDeclaredConstructors();
            for(Constructor con : cons) {
                System.out.println(con);
            }
            System.out.println("--------");
    
            //Constructor<T> getConstructor(Class<?>... parameterTypes) 返回一个 Constructor对象,该对象反映由该 Class对象表示的类的指定公共构造函数
            //Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes) 返回一个 Constructor对象,该对象反映由此 Class对象表示的类或接口的指定构造函数
            //参数:你要获取的构造方法的参数的个数和数据类型对应的字节码文件对象
    
            Constructor<?> con = c.getConstructor();
    
            //Constructor提供了一个类的单个构造函数的信息和访问权限
            //T newInstance(Object... initargs) 使用由此 Constructor对象表示的构造函数,使用指定的初始化参数来创建和初始化构造函数的声明类的新实例
            Object obj = con.newInstance();
            System.out.println(obj);
    
    //        Student s = new Student();
    //        System.out.println(s);
        }
    }
    
7.3.2Constructor类用于创建对象的方法
方法名说明
T newInstance(Object…initargs)根据指定的构造方法创建对象

7.4反射获取构造方法并使用练习1【应用】

  • 案例需求

    • 通过反射获取公共的构造方法并创建对象
  • 代码实现

    • 学生类

      public class Student {
          //成员变量:一个私有,一个默认,一个公共
          private String name;
          int age;
          public String address;
      
          //构造方法:一个私有,一个默认,两个公共
          public Student() {
          }
      
          private Student(String name) {
              this.name = name;
          }
      
          Student(String name, int age) {
              this.name = name;
              this.age = age;
          }
      
          public Student(String name, int age, String address) {
              this.name = name;
              this.age = age;
              this.address = address;
          }
      
          //成员方法:一个私有,四个公共
          private void function() {
              System.out.println("function");
          }
      
          public void method1() {
              System.out.println("method");
          }
      
          public void method2(String s) {
              System.out.println("method:" + s);
          }
      
          public String method3(String s, int i) {
              return s + "," + i;
          }
      
          @Override
          public String toString() {
              return "Student{" +
                      "name='" + name + '\'' +
                      ", age=" + age +
                      ", address='" + address + '\'' +
                      '}';
          }
      }
      
    • 测试类

      public class ReflectDemo02 {
          public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
              //获取Class对象
              Class<?> c = Class.forName("com.itheima_02.Student");
      
              //public Student(String name, int age, String address)
              //Constructor<T> getConstructor(Class<?>... parameterTypes)
              Constructor<?> con = c.getConstructor(String.class, int.class, String.class);
              //基本数据类型也可以通过.class得到对应的Class类型
      
              //T newInstance(Object... initargs)
              Object obj = con.newInstance("林青霞", 30, "西安");
              System.out.println(obj);
          }
      }
      

7.5反射获取构造方法并使用练习2【应用】

  • 案例需求

    • 通过反射获取私有构造方法并创建对象
  • 代码实现

    • 学生类:参见上方学生类

    • 测试类

      public class ReflectDemo03 {
          public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
              //获取Class对象
              Class<?> c = Class.forName("com.itheima_02.Student");
      
              //private Student(String name)
              //Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes)
              Constructor<?> con = c.getDeclaredConstructor(String.class);
      
              //暴力反射
              //public void setAccessible(boolean flag):值为true,取消访问检查
              con.setAccessible(true);
      
              Object obj = con.newInstance("林青霞");
              System.out.println(obj);
          }
      }
      

7.6反射获取成员变量并使用【应用】

7.6.1Class类获取成员变量对象的方法
  • 方法分类

    方法名说明
    Field[] getFields()返回所有公共成员变量对象的数组
    Field[] getDeclaredFields()返回所有成员变量对象的数组
    Field getField(String name)返回单个公共成员变量对象
    Field getDeclaredField(String name)返回单个成员变量对象
  • 示例代码

    public class ReflectDemo01 {
        public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
            //获取Class对象
            Class<?> c = Class.forName("com.itheima_02.Student");
    
            //Field[] getFields() 返回一个包含 Field对象的数组, Field对象反映由该 Class对象表示的类或接口的所有可访问的公共字段
            //Field[] getDeclaredFields() 返回一个 Field对象的数组,反映了由该 Class对象表示的类或接口声明的所有字段
    //        Field[] fields = c.getFields();
            Field[] fields = c.getDeclaredFields();
            for(Field field : fields) {
                System.out.println(field);
            }
            System.out.println("--------");
    
            //Field getField(String name) 返回一个 Field对象,该对象反映由该 Class对象表示的类或接口的指定公共成员字段
            //Field getDeclaredField(String name) 返回一个 Field对象,该对象反映由该 Class对象表示的类或接口的指定声明字段
            Field addressField = c.getField("address");
    
            //获取无参构造方法创建对象
            Constructor<?> con = c.getConstructor();
            Object obj = con.newInstance();
    
    //        obj.addressField = "西安";
    
            //Field提供有关类或接口的单个字段的信息和动态访问
            //void set(Object obj, Object value) 将指定的对象参数中由此 Field对象表示的字段设置为指定的新值
            addressField.set(obj,"西安"); //给obj的成员变量addressField赋值为西安
    
            System.out.println(obj);
    
    
    
    //        Student s = new Student();
    //        s.address = "西安";
    //        System.out.println(s);
        }
    }
    
7.6.2Field类用于给成员变量赋值的方法
方法名说明
voidset(Object obj,Object value)给obj对象的成员变量赋值为value

7.7反射获取成员变量并使用练习【应用】

  • 案例需求

    • 通过反射获取成员变量并赋值
  • 代码实现

    • 学生类:参见上方学生类

    • 测试类

      public class ReflectDemo02 {
          public static void main(String[] args) throws Exception {
              //获取Class对象
              Class<?> c = Class.forName("com.itheima_02.Student");
      
              //Student s = new Student();
              Constructor<?> con = c.getConstructor();
              Object obj = con.newInstance();
              System.out.println(obj);
      
              //s.name = "林青霞";
      //        Field nameField = c.getField("name"); //NoSuchFieldException: name
              Field nameField = c.getDeclaredField("name");
              nameField.setAccessible(true);
              nameField.set(obj, "林青霞");
              System.out.println(obj);
      
              //s.age = 30;
              Field ageField = c.getDeclaredField("age");
              ageField.setAccessible(true);
              ageField.set(obj,30);
              System.out.println(obj);
      
              //s.address = "西安";
              Field addressField = c.getDeclaredField("address");
              addressField.setAccessible(true);
              addressField.set(obj,"西安");
              System.out.println(obj);
          }
      }
      

7.8反射获取成员方法并使用【应用】

7.8.1Class类获取成员方法对象的方法
  • 方法分类

    方法名说明
    Method[] getMethods()返回所有公共成员方法对象的数组,包括继承的
    Method[] getDeclaredMethods()返回所有成员方法对象的数组,不包括继承的
    Method getMethod(String name, Class<?>… parameterTypes)返回单个公共成员方法对象
    Method getDeclaredMethod(String name, Class<?>… parameterTypes)返回单个成员方法对象
  • 示例代码

    public class ReflectDemo01 {
        public static void main(String[] args) throws Exception {
            //获取Class对象
            Class<?> c = Class.forName("com.itheima_02.Student");
    
            //Method[] getMethods() 返回一个包含 方法对象的数组, 方法对象反映由该 Class对象表示的类或接口的所有公共方法,包括由类或接口声明的对象以及从超类和超级接口继承的类
            //Method[] getDeclaredMethods() 返回一个包含 方法对象的数组, 方法对象反映由 Class对象表示的类或接口的所有声明方法,包括public,protected,default(package)访问和私有方法,但不包括继承方法
    //        Method[] methods = c.getMethods();
            Method[] methods = c.getDeclaredMethods();
            for(Method method : methods) {
                System.out.println(method);
            }
            System.out.println("--------");
    
            //Method getMethod(String name, Class<?>... parameterTypes) 返回一个 方法对象,该对象反映由该 Class对象表示的类或接口的指定公共成员方法
            //Method getDeclaredMethod(String name, Class<?>... parameterTypes) 返回一个 方法对象,它反映此表示的类或接口的指定声明的方法 Class对象
            //public void method1()
            Method m = c.getMethod("method1");
    
            //获取无参构造方法创建对象
            Constructor<?> con = c.getConstructor();
            Object obj = con.newInstance();
    
    //        obj.m();
    
            //在类或接口上提供有关单一方法的信息和访问权限
            //Object invoke(Object obj, Object... args) 在具有指定参数的指定对象上调用此 方法对象表示的基础方法
            //Object:返回值类型
            //obj:调用方法的对象
            //args:方法需要的参数
            m.invoke(obj);
    
    //        Student s = new Student();
    //        s.method1();
        }
    }
    
7.8.2Method类用于执行方法的方法
方法名说明
Objectinvoke(Object obj,Object… args)调用obj对象的成员方法,参数是args,返回值是Object类型

7.9反射获取成员方法并使用练习【应用】

  • 案例需求

    • 通过反射获取成员方法并调用
  • 代码实现

    • 学生类:参见上方学生类

    • 测试类

      public class ReflectDemo02 {
          public static void main(String[] args) throws Exception {
              //获取Class对象
              Class<?> c = Class.forName("com.itheima_02.Student");
      
              //Student s = new Student();
              Constructor<?> con = c.getConstructor();
              Object obj = con.newInstance();
      
              //s.method1();
              Method m1 = c.getMethod("method1");
              m1.invoke(obj);
      
              //s.method2("林青霞");
              Method m2 = c.getMethod("method2", String.class);
              m2.invoke(obj,"林青霞");
      
      //        String ss = s.method3("林青霞",30);
      //        System.out.println(ss);
              Method m3 = c.getMethod("method3", String.class, int.class);
              Object o = m3.invoke(obj, "林青霞", 30);
              System.out.println(o);
      
              //s.function();
      //        Method m4 = c.getMethod("function"); //NoSuchMethodException: com.itheima_02.Student.function()
              Method m4 = c.getDeclaredMethod("function");
              m4.setAccessible(true);
              m4.invoke(obj);
          }
      }
      

7.10反射的案例【应用】

7.10.1反射练习之越过泛型检查
  • 案例需求

    • 通过反射技术,向一个泛型为Integer的集合中添加一些字符串数据
  • 代码实现

    public class ReflectTest01 {
        public static void main(String[] args) throws Exception {
            //创建集合
            ArrayList<Integer> array = new ArrayList<Integer>();
    
    //        array.add(10);
    //        array.add(20);
    //        array.add("hello");
    
            Class<? extends ArrayList> c = array.getClass();
            Method m = c.getMethod("add", Object.class);
    
            m.invoke(array,"hello");
            m.invoke(array,"world");
            m.invoke(array,"java");
    
            System.out.println(array);
        }
    }
    
2.10.2运行配置文件中指定类的指定方法
  • 案例需求

    • 通过反射运行配置文件中指定类的指定方法
  • 代码实现

    public class ReflectTest02 {
        public static void main(String[] args) throws Exception {
            //加载数据
            Properties prop = new Properties();
            FileReader fr = new FileReader("myReflect\\class.txt");
            prop.load(fr);
            fr.close();
    
            /*
                className=com.itheima_06.Student
                methodName=study
             */
            String className = prop.getProperty("className");
            String methodName = prop.getProperty("methodName");
    
            //通过反射来使用
            Class<?> c = Class.forName(className);//com.itheima_06.Student
    
            Constructor<?> con = c.getConstructor();
            Object obj = con.newInstance();
    
            Method m = c.getMethod(methodName);//study
            m.invoke(obj);
        }
    }
    

8.模块化

8.1模块化概述【理解】

Java语言随着这些年的发展已经成为了一门影响深远的编程语言,无数平台,系统都采用Java语言编写。但是,伴随着发展,Java也越来越庞大,逐渐发展成为一门“臃肿” 的语言。而且,无论是运行一个大型的软件系统,还是运行一个小的程序,即使程序只需要使用Java的部分核心功能, JVM也要加载整个JRE环境。
为了给Java“瘦身”,让Java实现轻量化,Java 9正式的推出了模块化系统。Java被拆分为N多个模块,并允许Java程序可以根据需要选择加载程序必须的Java模块,这样就可以让Java以轻量化的方式来运行

其实,Java 7的时候已经提出了模块化的概念,但由于其过于复杂,Java 7,Java 8都一直未能真正推出,直到Java 9才真正成熟起来。对于Java语言来说,模块化系统是一次真正的自我革新,这种革新使得“古老而庞大”的Java语言重新焕发年轻的活力

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值