java学习笔记---7

63. Lambda表达式

1.函数式编程思想

在数学中,函数就是有输入量、输出量的一套计算方案,也就是“拿数据做操作”
面向对象思想强调“必须通过对象的形式来做事情”

  • 函数式思想则尽量忽略面向对象的复杂语法:“强调做什么,而不是以什么形式去做"而我们要学习的Lambda表达式就是函数式思想的体现

2.体验Lambda表达式

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

方式1:

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

public class MyRunnable implements Runnable{
    @Override
    public void run() {
        System.out.println("多线程程序启动了!");
    }
}

package Lambdas;

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

        //采用实现类的方法实现需求
        MyRunnable myRunnable = new MyRunnable();
        Thread thread = new Thread(myRunnable);
        thread.start();

    }
}

方法2:使用匿名内部类改进

package Lambdas;

public class LambdaDemo1 {
    public static void main(String[] args) {
        //使用匿名内部类实现
        new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("多线程程序启动了");
            }
        }).start();
    }
}

方法3:使用Lambda表达式改进

package Lambdas;

public class LambdaDemo2 {
    public static void main(String[] args) {
        //使用lambda表达是进行改进
        new Thread(()->{
            System.out.println("多线程实现!");
        }).start();
    }
}

3.Lambda表达式的格式

格式:

(形式参数)->{代码块}
  • 形式参数:如果有多个参数,参数之间用逗号隔开;
  • 如果没有参数,留空即可
  • ->:由英文中画线和大于符号组成,固定写法。代表指向动作
  • 代码块:是我们具体要做的事情,也就是以前我们写的方法体内容

4.Lambda表达式的使用前提(练习)

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

练习1:

  • 定义一个接口(Eatable),里面定义一个抽象方法: void eat();
  • 定义一个测试类(EatableDemo),在测试类中提供两个方法一个方法是: useEatable(Eatable e)
    —个方法是主方法,在主方法中调用useEatable方法
package Lambdas;

public interface Eatable {
    void eat();
}

package Lambdas;
//接口是抽象的,定义一个类去实现接口
public class EatableLmpl implements Eatable{
    @Override
    public void eat() {
        System.out.println("天天干土豆!");
    }
}

package Lambdas;

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

        //正常方法去调用一个接口: 先定义一个接口实现类,然后在测试类的主方法中去引入实现类对象 然后实现
        EatableLmpl e = new EatableLmpl();
        useEatable(e);

        //匿名内部类
        useEatable(new Eatable() {
            @Override
            public void eat() {
                System.out.println("豆豆豆豆");
            }
        });

        //Lambda表达狮
        useEatable(()->{
            System.out.println("hsdjhk");
        });

    }

    private static void useEatable(Eatable e) {
        e.eat();
    }
}

练习2:

  • 定义一个接口(Flyable),里面定义一个抽象方法: void fly(String s);
  • 定义一个测试类(FlyableDemo),在测试类中提供两个方法
    一个方法是: useFlyable(Flyablef)
    —个方法是主方法,在主方法中调用useFlyable方法
package Lambdas;

public interface Flyable {
    void fly(String s);
}

package Lambdas;

public class FlyableDemo {
    public static void main(String[] args) {
        useFlyable((String s)->{
            System.out.println(s);
        });
    }

    private static void useFlyable(Flyable f) {
        f.fly("小鸟");
    }
}

练习3:

  • 定义一个接口(Addable),里面定义一个抽象方法: intadd(intx,int y);
  • 定义一个测试类(AddableDemo),在测试类中提供两个方法
    一个方法是: useAddable(Addable a)
    一个方法是主方法,在主方法中调用useAddable方法
package Lambdas;

public interface Addable {
    int add(int x,int y);
}

package Lambdas;

public class AddableDemo {
    public static void main(String[] args) {
        useAddable((int x,int y)->{
            return x+y;
        });
    }

    private static void useAddable(Addable a) {
        int sum = a.add(10,20);
        System.out.println(sum);
    }
}

5.Lamdba表达式省略模式

  • 参数的类型可以省略
    在这里插入图片描述

  • 如果参数有且仅有一个 小括号也可以省略
    在这里插入图片描述

  • 如果代码只有一条,可以省略大括号和分号,如果有return 也可以省略
    在这里插入图片描述
    在这里插入图片描述

6 Lambda表达式使用注意事项

  • 必须要有接口,且接口中有且仅有一个抽象方法;
  • 必须要有上下文环境,才能推到出Lambda对应接口;
 //上下文环境
        
        //局部变量赋值得知Lambda接口
        Runnable r = ()-> System.out.println("你好");
        new Thread(r).start();

        //根据方法的参数得知Lambda对应接口
        new Thread(()-> System.out.println("咋了")).start();

7.Lambda表达式和匿名内部类的区别

所需类型不同

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

使用限制不同

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

实现原理不同

  • 匿名内部类:编译之后,产生一个单独的.class字节码文件

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

  • 准备一个接口、抽象类、具体类

  • 采用匿名内部类分别调用他们中的方法

package Lambdas;

public interface JIekou {
    void method();
}

package Lambdas;

public class JuTi {
    public void show(){}
}

package Lambdas;

public abstract class ChouXiang {
    void fangfa(){}
}

package Lambdas;

public class NiMingDemo {
    public static void main(String[] args) {
        useJieKou(new JIekou() {
            @Override
            public void method() {
                System.out.println("接口");
            }
        });
        useJuti(new JuTi(){
            @Override
            public void show() {
                System.out.println("具体类");
            }
        });
        useChouXiang(new ChouXiang() {
            @Override
            void fangfa() {
                System.out.println("抽象类");
            }
        });
    }

    private static void useJuti(JuTi juTi) {
        juTi.show();
    }

    private static void useChouXiang(ChouXiang chouXiang) {
        chouXiang.fangfa();
    }

    private static void useJieKou(JIekou jIekou) {
        jIekou.method();
    }
}

在这里插入图片描述

package Lambdas;

public class LambdaDemo4 {
    public static void main(String[] args) {
//        useChouXiang(()-> {System.out.println("抽象")};);
        useJieKou(()-> System.out.println("接口"));
//        useJuti(()-> System.out.println("具体"));
    }
    private static void useJuti(JuTi juTi) {
        juTi.show();
    }

    private static void useChouXiang(ChouXiang chouXiang) {
        chouXiang.fangfa();
    }

    private static void useJieKou(JIekou jIekou) {
        jIekou.method();
    }
}

在这里插入图片描述

64.接口的组成更新

1.接口组成更新概述接口的组成

常量
public static final

抽象方法
public abstract

  • 默认方法(Java 8)
  • 静态方法(Java8)
  • 私有方法(Java 9)

2.接口中默认方法

接口中默认方法的定义格式:

  • 格式: public default返回值类型方法名(参数列表){}
    范例: public default void show30 { }

接口中默认方法的注意事项:
默认方法不是抽象方法,所以不强制被重写。但是可以被重写,重写的时候去掉default关键字public可以省略,default不能省略

package Interfaces;

public interface MyInterface {
    void show1();
    void sho2();

    default void show3(){
        System.out.println("show3");
    }
}

package Interfaces;

public class MyInterface1 implements MyInterface{
    @Override
    public void show1() {
        System.out.println("one show1");
    }

    @Override
    public void sho2() {
        System.out.println("one show2");
    }
}

package Interfaces;

public class MyInterfaceDemo {
    public static void main(String[] args) {
        MyInterface m1 = new MyInterface1();
        m1.show1();
        m1.sho2();
        m1.show3();
    }
}

在这里插入图片描述

3.接口中静态方法

接口中静态方法的定义格式:

  • 格式: public static返回值类型方法名(参数列表){}
  • 范例: public static void show){ }

接口中静态方法的注意事项:
静态方法只能通过接口名调用,不能通过实现类名或者对象名调用public可以省略,static不能省略

静态方法不能调用非静态方法

package Interfaces;

public interface MyInterface {
    void show1();
    void sho2();

    default void show3(){
        System.out.println("show3");
    }

    public static void show4(){
        System.out.println("接口中的静态方法");
    }
}

package Interfaces;

public class MyInterfaceDemo {
    public static void main(String[] args) {
        MyInterface m1 = new MyInterface1();
        m1.show1();
        m1.sho2();
        m1.show3();

//        m1.show4();
        MyInterface.show4();
    }
}

在这里插入图片描述

4.接口中私有方法

接口中私有方法的定义格式:

  • 格式1: private返回值类型方法名(参数列表){}
  • 范例1: private void show) { }
  • 格式2: private static返回值类型方法名(参数列表)
  • {}范例2: private static void method){ }

接口中私有方法的注意事项:
默认方法可以调用私有的静态方法和非静态方法静态方法只能调田私有的静杰方法

package Interfaces;

public interface Inter {
    //两个默认方法
    default void show1(){
        System.out.println("show1 start");
//        System.out.println("1");
//        System.out.println("2");
//        System.out.println("3");
        common();
        System.out.println("show1 end");
    }

    default void show2(){
        System.out.println("show2 start");
        System.out.println("1");
        System.out.println("2");
        System.out.println("3");
        System.out.println("show2 end");
    }
    
    //默认方法可以掉调用私有方法
    private void common(){
        System.out.println("1");
        System.out.println("2");
        System.out.println("3");
    }
    //一个静态方法
    //静态方法可以调用静态私有方法
    static void show3(){
        System.out.println("show3 start");
//        System.out.println("1");
//        System.out.println("2");
//        System.out.println("3");
        common1();
        System.out.println("show3 end");
    }

    private static void common1(){
        System.out.println("1");
        System.out.println("2");
        System.out.println("3");
    }
    
}

65.方法引用

1.体验方法引用
在使用Lambda表达式的时候,我们实际上传递进去的代码就是一种解决方案:拿参数做操作。
那么考虑一种情况:如果我们在Lambda中所指定的操作方案,已经有地方存在相同方案,没有必要再写重复逻辑,通过方法引用来使用已经存在的方案

package Lambdas;

public interface Printable {
    void printString(String s);
}

package Lambdas;

public class PrintableDemo {
    public static void main(String[] args) {
        //采用Lambda
        usePrint(s-> System.out.println(s));

        //方法引用符
        usePrint(System.out::println);

    }

    private static void usePrint(Printable p) {
        p.printString("ppppp");
    }
}

2.方法引用符

方法引用符

  • :: 该符号为引用运算符,而它所在的表达式被称为方法引用
  • Lambda表达式: usePrintable(s-> System.out.println(s));
  • 方法引用: usePrintable(System.out:println);

分析:拿到参数s之后通过Lambda表达式,传递给System.out.println方法去处理
分析:直接使用System.out中的println方法来取代Lambda,代码更加的简洁

推导与省略

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

3.Lambda表达式支持的方法引用

常见的引用方式:

  • 引用类方法
  • 引用对象的实例方法
  • 引用类的实例方法
  • 引用构造器

3.1 引用类方法

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

  • 格式: 类名::静态方法
  • 范例: Integer::parselnt
    Integer类的方法: public static int parselnt(String s)将此String转换为int类型数据

练习:

  • 定义一个接口(Converter),里面定义一个抽象方法int convert(String s);
  • 定义一个测试类(ConverterDemo),在测试类中提供两个方法
    一个方法是: useConverter(Converter c)
    一个方法是主方法,在主方法中调用useConverter方法
package Quote;

public interface Converter {
    int convert(String s);
}

package Quote;

public class ConverterDemo {
    public static void main(String[] args) {
        //采用Lambda表达式
        useConverter(s -> {return Integer.parseInt(s);});

        //引用类
        useConverter(Integer::parseInt);

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

    private static void useConverter(Converter c) {
        int convert = c.convert("666");
        System.out.println(convert);
    }
}

3.2 引用对象的实例方法

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

  • 格式: 对象::成员方法
  • 范例: “HelloWorld”::toUpperCase
    String类中的方法: public String toUpperCase()将此String所有字符转换为大写

练习

  • 定义一个类(PrintString),里面定义一个方法
    public void printUpper(String s):把字符串参数变成大写的数据,然后在控制台输出
  • 定义一个接口(Printer),里面定义一个抽象方法
    void printUpperCase(Strings)
  • 定义一个测试类(PrinterDemo),在测试类中提供两个方法
    一个方法是: usePrinter(Printer p)
    一个方法是主方法,在主方法中调用usePrinter方法
package Quote;

public interface Print {
    void printUpperCase(String s);
}

package Quote;

public class PrintString {
    public void printUpper(String s){
        String s1 = s.toUpperCase();
        System.out.println(s1);

    }
}

package Quote;

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

        //Lambda表达式
        usePrinter(s-> System.out.println(s.toUpperCase()));  //WORLD HELLO


        //引用对象的实例方法
        PrintString ps = new PrintString();    //WORLD HELLO
        usePrinter(ps::printUpper);


    }

    private static void usePrinter(Print p) {
        p.printUpperCase("World Hello");
    }
}

3.3 引用类的实例方法

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

  • 格式:类名 :: 成员方法
  • 范例: String :: substring
    String类中的方法: public String substring(int beginIndex,int endIndex)
    从beginIndex开始到endIndex结束,截取字符串。返回一个子串,子串的长度为endndex-beginlndex

练习

  • 定义一个接口(MyString),里面定义一个抽象方法:
    String mySubString(String s,int x,int y);
  • 定义一个测试类(MyStringDemo),在测试类中提供两个方法
    一个方法是:useMyString(MyString my)
    一个方法是主方法,在主方法中调用useMyString方法
package Quote;

public interface MyString {
    String myString (String s,int x,int y);
}

package Quote;

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

        //采用lambda表达式
        useMyString((s, x, y) -> s.substring(x,y));  //ello

        //引用类实例方法
        useMyString(String::substring);    //ello

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

    }

    private static void useMyString(MyString my) {
        String s = my.myString("helloworld", 1, 5);
        System.out.println(s);

    }
}

3.4 引用构造器

引用构造器,其实就是引用构造方法

  • 格式:类名::new
  • 范例: Student::new

练习

  • 定义一个类(Student),里面有两个成员变量(name,age)
    并提供无参构造方法和带参构造方法,以及成员变量对应的get和set方法
  • 定义一个接口(StudentBuilder),里面定义一个抽象方法
    Student build(String name,int age);
  • 定义一个测试类(StudentDemo),在测试类中提供两个方法
    一个方法是: useStudentBuilder(StudentBuilder s)
    一个方法是主方法,在主方法中调用useStudentBuilder方法
package Quote;

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

    public Student() {
    }

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

    public int getAge() {
        return age;
    }

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

    public String getName() {
        return name;
    }

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

package Quote;

public interface StudentBulider {
    Student build(int age,String name);
}

package Quote;

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

        //采用lambda表达式
        useStudentBuilder((i,s)->{      //nini,22
            Student st = new Student(i,s);
            return st;
        });

        //采用引用构造器
        useStudentBuilder(Student::new);   //nini,22
    }

    private static void useStudentBuilder(StudentBulider sb) {
        Student s = sb.build(22, "nini");
        System.out.println(s.getName()+","+s.getAge());
    }
}

66.函数式接口

1.概述:
函数式接口:有且仅有一个抽象方法的接口;

函数式接口就是可以体现Lambda表达式使用的接口

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

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

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

package FunctionalInterfaces;

@FunctionalInterface  //通过这个来体现函数式接口
// 在函数式接口中有且仅有一个抽象方法
public interface MyInterface {
    void show();
}

package FunctionalInterfaces;

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

        MyInterface m = ()-> System.out.println("函数式接口");
        m.show();
    }
}

2.函数式接口作为方法的参数

需求

  • 定义一个类(RunnableDemo),在类中提供两个方法
    一个方法是: startThread(Runnable r)方法参数Runnable是一个函数式接口
    一个方法是主方法,在主方法中调用startThread方法

如果方法的参数是一个函数式接口,我们可以使用Lambda表达式作为参数传递 startThread((->
System.out.println(Thread.currentThread().getName()+“线程启动了”);

package FunctionalInterfaces;

public class RunnableDemo {
    public static void main(String[] args) {
        //采用匿名内部类
        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) {
//        Thread t = new Thread(r);
//        t.start();
        new Thread(r).start();
    }
}

3.函数式接口作为方法的返回值

需求

  • 定义一个类(ComparatorDemo),在类中提供两个方法
    一个方法是:Comparator getComparator()方法返回值Comparator是
    一个函数式接口一个方法是主方法,在主方法中调用getComparator方法

如果方法的返回值是一个函数式接口,我们可以使用Lambda表达式作为结果返回

  • private static Comparator getComparator) {
    return(s1, s2) -> s1.length() - s2.length);

关于匿名内部类:https://blog.csdn.net/qq_35427589/article/details/124212864

package FunctionalInterfaces;

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

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

        //构造使用场景
        ArrayList<String> al = new ArrayList<>();

        al.add("ccc");
        al.add("aa");
        al.add("ddddd");
        al.add("b");

//        Collections.sort(al);  //自然排序 [aa, b, ccc, ddddd]
  //      System.out.println(al);

        Collections.sort(al,getComparator());
        System.out.println(al);   //采用匿名内部类 根据指定长度进行排序[b, aa, ccc, ddddd]
    }

    private static Comparator<String> getComparator() {
        //使用匿名内部类
        Comparator<String> comp = new Comparator<String>(){
            @Override
            public int compare(String o1, String o2) {
                return o1.length()-o2.length();
            }
        };
        return comp;
    }
}

package FunctionalInterfaces;

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

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

        //构造使用场景
        ArrayList<String> al = new ArrayList<>();

        al.add("ccc");
        al.add("aa");
        al.add("ddddd");
        al.add("b");

        Collections.sort(al,getComparator());
        System.out.println(al);   //[b, aa, ccc, ddddd]
    }

    private static Comparator<String> getComparator() {
        //使用lambda
        return (String s1,String s2)->{
            return s1.length()-s2.length();
        };
    }
}

package FunctionalInterfaces;

import java.util.function.Supplier;

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

        //通过lambda表达式
        String s = getString(() -> "nini");
        System.out.println(s);


        Integer i = getInterge(() -> 10);
        System.out.println(i);

    }
    
    //返回一个字符串
    private static String getString(Supplier<String> sup) {
        return sup.get();
    }
    //返回一个整数
    private static Integer getInterge(Supplier<Integer> sup) {
        return sup.get();
    }



}

4.常用的函数式接口

  • Supplier接口
  • Consumer接口
  • Predicate接口
  • Function接口

4.1 Supplier接口

Supplier:包含一个无参的方法
get(): 获得结果
该方法不需要参数,它会按照某种实现逻辑(由Lambda表达式实现)返回一个数据
Supplier接口也被称为生产型接口,如果我们指定了接口的泛型是什么类型,那么接口中的get方法就会生产什么类型的数据供我们使用心

package FunctionalInterfaces;

import java.util.function.Supplier;

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

        //通过lambda表达式
        String s = getString(() -> "nini");
        System.out.println(s);


        Integer i = getInterge(() -> 10);
        System.out.println(i);

    }

    //返回一个字符串
    private static String getString(Supplier<String> sup) {
        return sup.get();
    }
    //返回一个整数
    private static Integer getInterge(Supplier<Integer> sup) {
        return sup.get();
    }



}

Supplier练习:

  • 定义一个类(SupplierTest),在类中提供两个方法
    一个方法是: int getMax(Supplier sup)用于返回一个int数组中的最大值
    一个方法是主方法,在主方法中调用getMax方法
package FunctionalInterfaces;

import java.util.ArrayList;
import java.util.function.Supplier;

public class SupplierTest {
    public static void main(String[] args) {
        //定义数组
        int[] arr = {1,59,80,63,5,10,25};

        int max1 = 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(max1);  //80
    }

    private static int getMax(Supplier<Integer> sup) {
        return sup.get();
    }
}

4.2 Consumer接口

Consumer:包含两个方法

  • void accept(T t):对给定的参数执行此操作
  • defaultConsumer andThen(Consumer after):返回一个组合的Consumer,依次执行此操作,然后执行after操作Consumer接口也被称为消费型接口,它消费的数据由泛型指定
package FunctionalInterfaces;

import java.util.function.Consumer;

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

        operatorString("nini",(String s)->{
            System.out.println(s);
        });


        operatorString("vivi",s-> System.out.println(s));  //简写
        operatorString("coco",System.out::println);   //方法引用
    }

//消费字符串
    private static void operatorString(String name,Consumer<String> com) {
        com.accept(name);
    }
}

package FunctionalInterfaces;

import java.util.function.Consumer;

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

        operatorString("vivi",s-> System.out.println(s));
        operatorString("coco",s-> System.out.println(s),s-> System.out.println(new StringBuilder(s).reverse().toString()));

    }

    private static void operatorString(String name,Consumer<String> com) {
        com.accept(name);
    }
    //同一个方法 用不同的方式消费同一个字符串 方法重载
    private static void operatorString(String name,Consumer<String> com,Consumer<String> cos) {
//        cos.accept(name);
//        com.accept(name);
        com.andThen(cos).accept(name);
    }
}

在这里插入图片描述
练习

String[] strArray = {“林青霞,30”,“张曼玉,35”,“王祖贤,33”};
字符串数组中有多条信息,请按照格式:“姓名:XX年龄:XX"的格式将信息打印出来.

要求:
把打印姓名的动作作为第一个Consumer接口的Lambda实例
把打印年龄的动作作为第二个Consumer接口的Lambda实例将两个Consumer接口按照顺序组合到一起使用

package FunctionalInterfaces;

import java.util.function.Consumer;

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

        //方法1 在方法中分割开年龄和姓名
        
        String[] strArray = {"nini,12","vvi,89","coco,23"};
        getNameAge(strArray,( c1)->System.out.print("姓名:"+c1), (c2)->System.out.println(",年龄:"+c2));
    }

    private static void getNameAge(String[] s, Consumer<String> c1,Consumer<String> c2) {
        for(String str:s){
            String[] ss = str.split(",");
            c1.accept(ss[0]);
            c2.accept(ss[1]);
        }
    }
}
//姓名:nini,年龄:12
//姓名:vvi,年龄:89
//姓名:coco,年龄:23
package FunctionalInterfaces;

import java.util.function.Consumer;

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

        //方法2 在lambda表达式中分割开年龄和姓名

        String[] strArray = {"nini,12","vvi,89","coco,23"};
        getNameAge(strArray,(c1)->System.out.print("姓名:"+c1.split(",")[0]), (c2)->System.out.println(",年龄:"+c2.split(",")[1]));
    }

    private static void getNameAge(String[] s, Consumer<String> c1,Consumer<String> c2) {
        for(String str:s){
            c1.andThen(c2).accept(str);
        }
    }
}
//姓名:nini,年龄:12
//姓名:vvi,年龄:89
//姓名:coco,年龄:23

4.3 Predicate接口

Predicate:常用的四个方法

  • boolean test(T t):对给定的参数进行判断(判断逻辑由Lambda表达式实现),返回一个布尔值
  • default Predicate negate):返回一个逻辑的否定,对应逻辑非
  • default Predicate and(Predicate other):返回一个组合判断,对应短路与(&&)
  • default Predicate or(Predicate other):返回一个组合判断,对应短路或
  • Predicate接口通常用于判断参数是否满足指定的条件(||)
package FunctionalInterfaces;

// boolean test(T t):对给定的参数进行判断(判断逻辑由Lambda表达式实现),返回一个布尔值

import java.util.function.Predicate;

public class PredicateDemo {
    public static void main(String[] args) {
        boolean b = checkString("hello", (s) -> {
            return s.length() > 8;
        });
        System.out.println(b);   //false
    }

    //判断给定的字符是否满足条件
    private static boolean checkString(String s, Predicate<String> pre){
        return pre.test(s);
    }
}

package FunctionalInterfaces;

// default Predicate<T> negate):返回一个逻辑的否定,对应逻辑非

import java.util.function.Predicate;

public class PredicateDemo {
    public static void main(String[] args) {
        boolean b = checkString("hello", (s) -> {
            return s.length() > 8;
        });
        System.out.println(b);   //true
    }

    //判断给定的字符是否满足条件
    private static boolean checkString(String s, Predicate<String> pre){
//        return !pre.test(s);
        return pre.negate().test(s);  //进行逻辑非操作
    }
}

package FunctionalInterfaces;

// - default Predicate<T> and(Predicate other):返回一个组合判断,对应短路与
//- default Predicate<T> or(Predicate other):返回一个组合判断,对应短路或

import java.util.function.Predicate;

public class PredicateDemo {
    public static void main(String[] args) {
        boolean b = checkString("hello",( s -> s.length() > 8),(s->s.length()<15));
        System.out.println(b);   //false
    }

    //同一个字符给出两个不同的判断条件,最后吧两个判断结果做逻辑与运算
    private static boolean checkString(String s, Predicate<String> pre1,Predicate<String> pre2){
        boolean b1 = pre1.test(s);
        boolean b2 = pre2.test(s);
        boolean b = b1 && b2;
        return b;
      //return pre1.and(pre2).test(s);
    }

    //判断给定的字符是否满足条件
    private static boolean checkString(String s, Predicate<String> pre){
        return pre.test(s);
    }
}

练习

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

字符串数组中有多条信息,请通过Predicate接口的拼装将符合要求的字符串筛选到集合Arraylist中,并遍历ArrayList集合同时满足如下要求:姓名长度大于2;年龄大于33

分析
有两个判断条件,所以需要使用两个Predicate接口,对条件进行判断必须同时满足两个条件,所以可以使用and方法连接两个判断条件

package FunctionalInterfaces;

import java.util.ArrayList;
import java.util.function.Predicate;

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

        //遍历ArrayList集合同时满足如下要求:姓名长度大于2;年龄大于33

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

        ArrayList<String> arr = studentChoose(strArray, s -> s.split(",")[0].length() > 2, s -> (Integer.parseInt(s.split(",")[1])) < 33);
        for(String s:arr){
            System.out.println(s);  //林青霞,30
        }

    }
    //通过Predicate接口的拼接将符合要求的字符串筛选到集合中
    private static ArrayList<String> studentChoose(String[] s, Predicate<String> pre1, Predicate<String> pre2){
        ArrayList<String> arrayList = new ArrayList<String>();
        for(String ss:s){
             if(pre1.and(pre2).test(ss)){
                 arrayList.add(ss);
             }
        }
        return arrayList;
    }
}

4.4 Function接口

Function<T,R>:常用的两个方法

  • R apply(T t):将此函数应用于给定的参数
  • default Function andThen(Function after):返回一个组合函数,首先将该函数应用于输入,然后将after函数应用于结果
  • Function<T,R>接口通常用于对参数进行处理,转换(处理逻辑由Lambda表达式实现),然后返回一个新的值
package FunctionalInterfaces;

import java.util.function.Function;

//- R apply(T t):将此函数应用于给定的参数
public class FunctionDemo {
    public static void main(String[] args) {

        //定义一个方法,把一个字符串转换int类型,在控制台输出
        stringToInt("100",s->Integer.parseInt(s));
        stringToInt("101",Integer::parseInt);  //方法引用

        //定义一个方法,把一个int类型的数据加上一个整数之后,转为字符串在控制台输出
        stringToIntAdd(100,s->String.valueOf(s+5));

        //定义一个方法,把一个字符串转换int类型,把int类型的数据加上一个整数之后,转为字符串在控制台输出
        stringToIntAdd2("100",s->String.valueOf(Integer.parseInt(s)+50));
        stringToIntAdd2("500",s->Integer.parseInt(s),i->String.valueOf(i+50));
    }

    private static void stringToInt(String s, Function<String,Integer> f) {
        System.out.println(f.apply(s));
    }

    private static void stringToIntAdd(int s, Function<Integer,String> f) {
        System.out.println(f.apply(s));
    }

    private static void stringToIntAdd2(String s, Function<String,String> f) {
        System.out.println(f.apply(s));
    }

    //通过两个步骤去完成需求3
    private static void stringToIntAdd2(String s, Function<String,Integer> f1,Function<Integer,String> f2) {
//        Integer i = f1.apply(s);
//        String ss = f2.apply(i);
//        System.out.println(ss);
        String ss = f1.andThen(f2).apply(s);
        System.out.println(ss);
    }
}

练习

String s =“林青霞,30”;

要求:

  1. 将字符串截取得到数字年龄部分
  2. 将上一步的年龄字符串转换成为int类型的数据
  3. 将上一步的int数据加70,得到一个int结果,在控制台输出请通过Function接口来实现函数拼接
package FunctionalInterfaces;

import java.util.function.Function;

//1. 将字符串截取得到数字年龄部分
//2. 将上一步的年龄字符串转换成为int类型的数据
//3. 将上一步的int数据加70,得到一个int结果,在控制台输出请通过Function接口来实现函数拼接
public class FunctionStudent {
    public static void main(String[] args) {

        String st ="林青霞,30";

        functionAge(st,s->(Integer.parseInt(s.split(",")[1]))+70);
    }

    private static void functionAge(String s, Function<String,Integer> f) {
        System.out.println(f.apply(s));
    }
}

package FunctionalInterfaces;

import java.util.function.Function;

//1. 将字符串截取得到数字年龄部分
//2. 将上一步的年龄字符串转换成为int类型的数据
//3. 将上一步的int数据加70,得到一个int结果,在控制台输出请通过Function接口来实现函数拼接
public class FunctionStudent {
    public static void main(String[] args) {

        String st ="林青霞,30";

        functionAge(st,s->s.split(",")[1],i->Integer.parseInt(i),ii->ii+70);
    }

    private static void functionAge(String s, Function<String,String> f1,Function<String,Integer> f2,Function<Integer,Integer> f3) {
//        String s1 = f1.apply(s);
//        Integer i = f2.apply(s1);
//        Integer ii = f3.apply(i);
//        System.out.println(ii);

        System.out.println(f1.andThen(f2).andThen(f3).apply(s));
    }
}

67.Stream流

1.体验Stream流

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

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

方法1:创建三个集合 分别遍历筛选

package Streams;

import java.util.ArrayList;

//- 创建一个集合,存储多个字符串元素
//- 把集合中所有以"张"开头的元素存储到一个新的集合
//- 把"张"开头的集合中的长度为3的元素存储到一个新的集合
//- 遍历上一步得到的集合
public class StreamZhangDemo {
    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> zhang = new ArrayList<String>();
        for(String s:list){
            if(s.startsWith("张")){
                zhang.add(s);
            }
        }

        ArrayList<String> threeZhang = new ArrayList<String>();
        for (String s:zhang){
            if(s.length()==3){
                threeZhang.add(s);
            }
        }

        for (String s:threeZhang){
            System.out.println(s);
        }

    }
}

方法2:Stream流

package Streams;

import java.util.ArrayList;

//- 创建一个集合,存储多个字符串元素
//- 把集合中所有以"张"开头的元素存储到一个新的集合
//- 把"张"开头的集合中的长度为3的元素存储到一个新的集合
//- 遍历上一步得到的集合
public class StreamZhangDemo {
    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(s-> System.out.println(s));
    }
}

2. Stream流的使用

  • 生成流
    通过数据源(集合,数组等)生成流,list.stream()
  • 中间操作
    一个流后面可以跟随零个或多个中间操作,其目的主要是打开流,做出某种程度的数据过滤/映射,然后返回一个新的流,交给下一个操作使用
    filter()
  • 终结操作
    一个流只能有一个终结操作,当这个操作执行后,流就被使用“光”了,无法再被操作。所以这必定是流的最后一个操作forEach0)

2.1 Stream流的生成方式

Stream流的常见生成方式

  • Collection体系 的集合可以使用默认方法stream()生成流
    default Stream stream()
  • Map体系 的集合间接的生成流
  • 数组 可以通过Stream接口的静态方法of(T… values)生成流
package Streams;

import java.util.*;
import java.util.stream.Stream;

//Collection体系 的集合可以使用默认方法stream()生成流
//Map体系 的集合间接的生成流
//数组 可以通过Stream接口的静态方法of(T… values)生成流
public class StreamGernerte {
    public static void main(String[] args) {
        //Collection体系 的集合可以使用默认方法stream()生成流
        ArrayList<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[] s = {"ici","cdsg","gdashg"};
        Stream<String> s1 = Stream.of(s);
        Stream<String> s2 = Stream.of("gshdj", "gfhasj");
        Stream<Integer> i = Stream.of(10, 20);
        
    }
}

2.2 Stream流的中间操作方法

  1. Stream filter (Predicate predicate):用于对流中的数据进行过滤
    Predicate接口中的方法:boolean test(T t):对给定的参数进行判断,返回一个布尔值

  2. Stream limit (long maxSize):返回此流中的元素组成的流,截取前指定参数个数的数据;

  3. Stream skip (long n):跳过指定参数个数的数据,返回由该流的剩余元素组成的流;

  4. static Stream concat(Stream a, Stream b):合并a和b两个流为一个流

  5. Stream distinct() 返回由该流的不同元素(根据Objectequals(Object))组成的流

  6. Stream sorted():返回由此流的元素组成的流,根据自然顺序排序

  7. Stream sorted(Comparator comparator):返回由该流的元素组成的流,根据提供的Comparator进行排序

  8. Stream map(Function mapper): 返回由给定函数应用于此流的元素的结果组成的流
    Function接口中的方法: R apply(T t)

  9. IntStream mapToInt(TolntFunction mapper): 返回一个ntStream其中包含将给定函数应用于此流的元素的结果

1~5 :filter、limit、skip、concat、distinct;

package Streams;

import java.util.ArrayList;
import java.util.stream.Stream;


public class StreamZhangDemo {
    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<T> filter(Predicate predicate):用于对流中的数据进行过滤
        list.stream().filter(s->s.startsWith("张")).filter(s->s.length()==3).forEach(s-> System.out.println(s));

        System.out.println("-------------------------------------------");
        // Stream<T> limit(long maxSize):返回此流中的元素组成的流,截取前指定参数个数的数据;
        list.stream().limit(3).forEach(System.out::println);  //输出前三个数据

        System.out.println("-------------------------------------------");
        //- Stream<T> skip(long n):跳过指定参数个数的数据,返回由该流的剩余元素组成的流
        list.stream().skip(3).forEach(System.out::println);    //跳过前三个数据

        System.out.println("---------------");
        list.stream().skip(2).limit(2).forEach(System.out::println);  //跳过前两个把剩下的元素的前两个输出

        //- static <T> Stream<T> concat(Stream a, Stream b):合并a和b两个流为一个流
        Stream<String> a = list.stream().limit(4);
        Stream<String> b = list.stream().skip(3);
//        Stream.concat(a,b).forEach(System.out::println);
        //- Stream<T> distinct():返回由该流的不同元素(根据Objectequals(Object))组成的流
        Stream.concat(a,b).distinct().forEach(System.out::println);
    }
}

6~7 : sorted

package Systems;

import java.util.ArrayList;

public class StreamSortDemo {
    public static void main(String[] args) {
        ArrayList<String> list = new ArrayList<String>();

        list.add("aj");
        list.add("nike");
        list.add("skertch");
        list.add("vans");
        list.add("vivo");
        list.add("oppo");

        //- Stream<T> sorted():返回由此流的元素组成的流,根据自然顺序排序
        list.stream().sorted().forEach(System.out::println);  //按字母顺序

        System.out.println("-------------------------------");
        //- Stream<T> sorted(Comparator comparator):返回由该流的元素组成的流,根据提供的Comparator进行排序
        list.stream().sorted((s1,s2)->s1.length()-s2.length()).forEach(System.out::println);  //按照长度排序
        System.out.println("-----------");
        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);   //当长度相同时按字母排序
    } 
}
//aj
//nike
//oppo
//skertch
//vans
//vivo
//-------------------------------
//aj
//nike
//vans
//vivo
//oppo
//skertch
//-----------
//aj
//nike
//oppo
//vans
//vivo
//skertch
//
//Process finished with exit code 0

8~9: map、mapToInt

package Streams;

import java.util.ArrayList;

//8. <R> Stream<R> map(Function mapper):返回由给定函数应用于此流的元素的结果组成的流
//Function接口中的方法: R apply(T t)
//10. IntStream mapToInt(TolntFunction mapper):返回一个ntStream其中包含将给定函数应用于此流的元素的结果
public class StreamIntString {
    public static void main(String[] args) {
        ArrayList<String> list = new ArrayList<String>();

        list.add("10");
        list.add("56");
        list.add("20");
        list.add("99");

        list.stream().map(s->Integer.parseInt(s)).forEach(System.out::println);  //将字符串集合转为int输出
//        list.stream().map(Integer::parseInt).forEach(System.out::println);

        list.stream().mapToInt(Integer::parseInt).forEach(System.out::println);  将字符串集合转为int输出
        int result = list.stream().mapToInt(Integer::parseInt).sum();
        System.out.println(result);  //求和

    }
}

2.3 Stream流的常见终结操作方法

  • void forEach(Consumer action):对此流的每个元素执行操作
    Consumer接口中的方法: void accept(T t):对给定的参数执行此操作
  • long count():返回此流中的元素数
 ArrayList<String> list = new ArrayList<String>();

        list.add("林青霞");
        list.add("张曼玉");
        list.add("王祖贤");
        list.add("柳岩");
        list.add("张敏");
        list.add("张无忌");

        //统计有几个张开头的
        long z = list.stream().filter(s->s.startsWith("张")).count();
        System.out.println(z);

2.4Stream流的练习

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

  • 男演员只要名字为3个字的前三人
  • 女演员只要姓林的,并且不要第一个
  • 把过滤后的男演员姓名和女演员姓名合并到一起
  • 把上一步操作后的元素作为构造方法的参数创建演员对象遍历数据
    演员类Actor已经提供,里面有一个成员变量,一个带参构造万法,以及成员变量
package Streams;

import java.util.ArrayList;
import java.util.stream.Stream;

//男演员只要名字为3个字的前三人
//- 女演员只要姓林的,并且不要第一个
//- 把过滤后的男演员姓名和女演员姓名合并到一起
//- 把上一步操作后的元素作为构造方法的参数创建演员对象遍历数据
public class StreamActorChoose {
    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<>();
        womanList.add("林心如");womanList.add("张曼玉");womanList.add("林青霞");womanList.add("柳岩");womanList.add("林志玲");womanList.add("王祖贤");

        Stream<String> manChoose = manList.stream().filter(s -> s.length() == 3).limit(3);
        Stream<String> womanChoose = womanList.stream().filter(s -> s.startsWith("林")).skip(1);

        Stream<String> endChoose = Stream.concat(manChoose, womanChoose);
        endChoose.map(s->{
            Actor actor = new Actor(s);
            return actor.getName();
        }).forEach(System.out::println);


    }
}
//周润发
//刘德华
//周星驰
//林青霞
//林志玲

3.Stream流的收集操作

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

Stream流的收集方法

  • R collect(Collector collector)
  • 但是这个收集方法的参数是一个Collector接口

工具类Collectors 提供了具体的收集方式

  • public static Collector toList():把元素收集到List集合中
  • public static Collector toSet():把元素收集到Set集合中
  • public static Collector toMap(Function keyMapper,Function valueMapper):把元素收集到Map集合中
package Streams;

import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;

public class StreamCollect {
    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<String> nameZ = list.stream().filter(s -> s.startsWith("张"));

        //public static <T> Collector toList():把元素收集到List集合中
        List<String> listName = nameZ.collect(Collectors.toList());
        for (String s:listName){
            System.out.print(s+" ");
        }

        System.out.println();
        //- public static <T> Collector toSet():把元素收集到Set集合中
        Stream<String> name3 = list.stream().filter(s -> s.length()==3);
        Set<String> setName = name3.collect(Collectors.toSet());
        for (String s:setName){
            System.out.print(s+" ");
        }
        //- public static Collector toMap(Function keyMapper,Function valueMapper):把元素收集到Map集合中
        String[] strArray = {"vovo,15","fsh,65","qwdrt,89"};
        Stream<String> arrStream = Stream.of(strArray).filter(s -> Integer.parseInt(s.split(",")[1]) > 20);
        Map<String, String> mapName = arrStream.collect(Collectors.toMap(s -> s.split(",")[0], s -> s.split(",")[1]));
        Set<Map.Entry<String, String>> entries = mapName.entrySet();
        for (Map.Entry<String, String> s:entries){
            System.out.println("姓名:"+s.getKey()+"年零:"+s.getValue());
        }

    }
}

68.反射

1.类加载

类的初始化:

当程序要使用某个类时,如果该类还未被加载到内存中,则系统会通过类的加载,类的连接,类的初始化这三个步骤来对类进行初始化。

//JVM连续完成这三个步骤,所以有时也把这三个步骤统称为类加载或者类初始化

  • 类的加载
    就是指将class文件读入内存,并为之创建一个java.lang.Class对象;
    //任何类被使用时,系统都会为之建立一个java.lang.Class对象

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

  • 类的初始化
    对类变量进行初始化;

1.1 类的初始化步骤

使用前提:

  • 假如类还未被加载和连接,则程序先加载并连接该类
  • 假如该类的直接父类还未被初始化,则先初始化其直接父类假如类中
  • 有初始化语句,则系统依次执行这些初始化语句

注意:在执行第2个步骤的时候,系统对直接父类的初始化步骤也遵循初始化步骤1-3

类的初始化时机:

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

1.2类加载器

类加载器的作用
负责将.class文件加载到内存中,并为之牛成对应的java.lang.Class对象

JVM的类加载机制

  • 全盘负责: 就是当一个类加载器负责加载某个Class时,该Class所依赖的和引用的其他Class也将由该类加载器负责载入,除非显示使用另外一个类加载器来载入
  • 父类委托: 就是当一个类加载器负责加载某个Class时,先让父类加载器试图加载该Class,只有在父类加载器无法加载该类时才尝试从自己的类路径中加载该类
  • 缓存机制: 保证所有加载过的Class都会被缓存,当程序需要使用某个Class对象时,类加载器先从缓存区中搜索该Class,只有当缓存区中不存在该Class对象时,系统才会读取该类对应的二进制数据,并将其转换成Class对象存储到缓存区

ClassLoader:是负责加载类的对象

Java运行时具有以下内置类加载器

  • Bootstrap class loader:它是虚拟机的内置类加载器,通常表示为null,并且没有父null
  • Platform class loader:平台类加载器可以看到所有平台类,平台类包括由平台类加载器或其祖先定义的Java SE平台API,其实现类和JDK特定的运行时类
  • System class loader:它也被称为应用程序类加载器,与平台类加载器不同。系统类加载器通常用于定义应用程序类路径,模块路径和JDK特定工具上的类
  • 类加载器的继承关系: System的父加载器为Platform,而Platform的父加载器为Bootstrap

ClassLoader中的两个方法

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

        ClassLoader c1 = c.getParent();
        System.out.println(c1);  //ExtClassLoader

        ClassLoader c2 = c1.getParent();
        System.out.println(c2);  //null
    }
}

//sun.misc.Launcher$AppClassLoader@18b4aac2
//sun.misc.Launcher$ExtClassLoader@1b6d3586
//null

2.反射

1.概述

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

在这里插入图片描述

2.2反射获取Class类的对象

我们要想通过反射去使用一个类,首先我们要获取到该类的字节码文件对象,也就是类型为Class类型的对象

三种方式获取Class类型的对象

  • 使用类的class属性来获取该类对应的Class对象。
    举例: Studentclass将会返回Student类对应的Class对象
  • 调用对象的getClass()方法,返回该对象所属类对应的Class对象
    该方法是Object类中的方法,所有的Java对象都可以调用该方法
  • 使用Class类中的静态方法forName(StringclassName),该方法需要传入字符串参数,该字符串参数的值是某个类的全路径,也就是完整包名的路径
package ClassLoader;
//使用类的class属性来获取该类对应的Class对象。
//调用对象的getClass()方法,返回该对象所属类对应的Class对象
//使用Class类中的静态方法forName(String className)

public class GetClassObject {
    public static void main(String[] args) throws ClassNotFoundException {
        //使用类的class属性来获取该类对应的Class对象
        Class<Student> c1 = Student.class;
        System.out.println(c1);   //输出Student类的包:class ClassLoader.Student 

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

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

2.3 反射获取类的构造方法并使用

Class类中用于获取构造方法的方法

  • Constructor<?>[getConstructors):返回所有公共构造方法对象的数组
  • Constructor<?>lgetDeclaredConstructors():返回所有构造方法对象的数组
  • Constructor getConstructor(Class<?> … parameterTypes):返回单个公共构造方法对象
  • Constructor getDeclaredConstructor(Class<?> … parameterTypes):返回单个构造方法对象

Constructor类中用于创建对象的方法

  • T newInstance(Object… initargs):根据指定的构造方法创建对象
package ClassLoader;

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

public class ReflectConstructionMethod {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
        //获取class对象
        Class<?> c = Class.forName("ClassLoader.Student");

        //反射获取构造方法
        Constructor<?>[] cons = c.getConstructors();   //只能拿到公共的
        for(Constructor cs:cons){
            System.out.println(cs);
        }

        Constructor<?>[] dCons = c.getDeclaredConstructors();  //拿到所有的构造方法
        for(Constructor cs:dCons){
            System.out.println(cs);
        }

        Constructor<?> con = c.getConstructor(); //输出单个无参构造方法
        Object obj = con.newInstance();
        System.out.println(obj);  //Student{name:null年龄:0地址:null

    }
}

练习1:通过反射实现如下操作

  • Student s = new Student(“林青霞”,30,“西安”);
  • system.out.println(s);
  • setAccessible(boolean flag):值为true,取消访问检查;
package ClassLoader;

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

public class ReflectDemo1 {
    public static void main(String[] args) throws NoSuchMethodException, ClassNotFoundException, InvocationTargetException, InstantiationException, IllegalAccessException {

        //获取Class对象
        Class<?> c = Class.forName("ClassLoader.Student");

        Constructor<?> cons = c.getConstructor(String.class,int.class,String.class);
        Object o = cons.newInstance("vivi", 18, "北京");
        System.out.println(o);
    }
}
//Student{name:vivi年龄:18地址:北京

练习2:通过反射实现如下操作

  • Student s = new Student(“林青霞”);
  • System.out.println(s);
package ClassLoader;

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

public class ReflectDemo1 {
    public static void main(String[] args) throws NoSuchMethodException, ClassNotFoundException, InvocationTargetException, InstantiationException, IllegalAccessException {

        //获取Class对象
        Class<?> c = Class.forName("ClassLoader.Student");

        Constructor<?> cons = c.getDeclaredConstructor(String.class);

        //暴力反射
        cons.setAccessible(true);
        Object o = cons.newInstance("vivi");  //私有构造方法
        System.out.println(o);
    }
}
//Student{name:vivi年龄:0地址:null

2.4 反射获取成员变量并使用

Class类中用于获取成员变量的方法

  • Field[] getFields():返回所有公共成员变量对象的数组
  • Field []getDeclaredFields):返回所有成员变量对象的数组Field
  • getField(String name):返回单个公共成员变量对象Field
  • getDeclaredField(String name):返回单个成员变量对象

Field类中用于给成员变量赋值的方法

  • void set(Object obj, Objectvalue):给obj对象的成员变量赋值为value
package ClassLoader;

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

public class ReflectDemo1 {
    public static void main(String[] args) throws NoSuchMethodException, ClassNotFoundException, InvocationTargetException, InstantiationException, IllegalAccessException, NoSuchFieldException {

        //获取Class对象
        Class<?> c = Class.forName("ClassLoader.Student");

        //获取公共的成员变量
        Field[] fields = c.getFields();
        for (Field field:fields){
            System.out.println(field);  //public java.lang.String ClassLoader.Student.address
        }

        //获取所有的变量
        Field[] declaredFields = c.getDeclaredFields();
        for (Field field:declaredFields){
            System.out.println(field);  //public java.lang.String ClassLoader.Student.address
        }

        //获取单个成员变量
        Field address = c.getField("address");
        //获取无参构造方法获取对象
        Constructor<?> con = c.getConstructor();
        Object o = con.newInstance();
        address.set(o,"上海");
        System.out.println(o);  //Student{name:null年龄:0地址:上海

    }
}


反射获取成员变量并使用练习

练习:通过反射实现如下操作

  • Student s = new Student();
  • s.name = “林青霞”;
  • s.age = 30;
  • s.address =“西安”;
  • System.out.println(s);
package ClassLoader;

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

public class ReflectDemo2 {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
        //获取Class对象
        Class<?> c = Class.forName("ClassLoader.Student");


        //获取无参构造方法获取对象
        Constructor<?> con = c.getConstructor();
        Object o = con.newInstance();
        System.out.println(o);

        //三个变量
        Field name = c.getDeclaredField("name");
        Field address = c.getField("address");
        Field age = c.getDeclaredField("age");
        
        address.set(o,"上海");
        name.setAccessible(true);  //暴力
        name.set(o,"林青霞");
        age.setAccessible(true);
        age.set(o,18);

        System.out.println(o);  //Student{name:林青霞年龄:18地址:上海

    }
}

2.5反射获取成员方法并使用

Class类中用于获取成员方法的方法

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

Method类中用于调用成员方法的方法

  • Object invoke(Objectobj, Object…args):调用obj对象的成员方法,参数是args,返回值是Object类型
package ClassLoader;

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

public class ReflectDemo3 {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
        //获取Class对象
        Class<?> c = Class.forName("ClassLoader.Student");

        //获取公共的成员方法(包括继承的)
        Method[] methods = c.getMethods();
        for(Method m:methods){
            System.out.println(m);
        }

        获取所有的成员方法(包括继承的)
        Method[] declaredMethods = c.getDeclaredMethods();
        for(Method m:declaredMethods){
            System.out.println(m);
        }

        //获取单个无参成员方法
        Method method1 = c.getMethod("method1");
        
        Constructor<?> con = c.getConstructor();
        Object o = con.newInstance();
        
        method1.invoke(o);  //method1


    }
}

练习:通过反射实现如下操作

  • Student s = new Student(;
  • s.method1();
  • s.method2(“林青霞”);
  • String ss = s.method3(“林青霞”,30);
  • System.out.printIn(ss);
  • s.function();
package ClassLoader;

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

public class ReflectDemo4 {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {

        Class<?> c = Class.forName("ClassLoader.Student");

        Constructor<?> con = c.getConstructor();
        Object o = con.newInstance();

        Method method1 = c.getMethod("method1");
        Method method2 = c.getMethod("method2", String.class);
        Method method3 = c.getMethod("method3", String.class, int.class);
        Method function = c.getDeclaredMethod("function");

        method1.invoke(o);
        method2.invoke(o,"nsisn");
        method3.invoke(o,"nini",18);
        function.setAccessible(true);
        function.invoke(o);
    }
}

3.反射练习

练习1:
通过反射在ArrayList集合中添加String对象

package ClassLoader;

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

public class ReflectDemo5 {
    public static void main(String[] args) throws InvocationTargetException, IllegalAccessException, NoSuchMethodException {

        ArrayList<Integer> arrayList = new ArrayList<Integer>();
        arrayList.add(10);
        arrayList.add(20);

        Class<? extends ArrayList> c = arrayList.getClass();
        Method addMethod = c.getMethod("add", Object.class);

        addMethod.invoke(arrayList,"nini");
        addMethod.invoke(arrayList,"vovi");

        System.out.println(arrayList);  //[10, 20, nini, vovi]
    }
}

练习2:
通过配置文件运行类中的方法

package ClassLoader;

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

/*
*       class.txt
*       className=xxx
*       methodName=xxx
* */
public class ReflectTest {
    public static void main(String[] args) throws IOException, ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException {
        //通过配置文件运行类中的方法


        //加载数据
        Properties prop = new Properties();
        FileReader fileReader = new FileReader("class.txt");
        prop.load(fileReader);
        fileReader.close();

        //根据建来获取值
        String className = prop.getProperty("className");
        String methodName = prop.getProperty("methodName");

        //通过反射来使用
        Class<?> c = Class.forName(className);  //反射出类地址
        Method method = c.getMethod(methodName);//反射出方法名

        Constructor<?> con = c.getConstructor();
        Object o = con.newInstance();
        method.invoke(o);  //daydayup!





    }
}

在这里插入图片描述
//如果需要运行教师类,只需要在文件中修改 值

69.模块化

1.概述: 运行java被拆分成多个模块,java程序根据需求加载相应的java模块,以实现轻量化运行;

//haha 又回到梦开始的地方了,话说学了这么就java 我都不会创建模块,全部都挤到一个默认的包的 src下。。。

2.模块的基本使用

  • 模块的创建
    在这里插入图片描述
  • 包的创建
    在这里插入图片描述

70.完结撒花了,啊哈~

80.笔记回顾

笔记1:https://blog.csdn.net/meini32/article/details/122195422
笔记2:https://blog.csdn.net/meini32/article/details/122321446
笔记3:https://blog.csdn.net/meini32/article/details/122381565
笔记4:https://blog.csdn.net/meini32/article/details/122784859
笔记5:https://blog.csdn.net/meini32/article/details/125783677
笔记6:https://blog.csdn.net/meini32/article/details/125895927

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值