欢迎来到我的博客

将来的你一定会感激现在拼搏的自己

JavaSE基础day25 反射2、1.8新特新


 

一. 反射应用

(一)  反射获取成员变量并使用

1.Class类获取成员变量对象:

 

Field[] getFields()

    返回所有公共成员变量对象的数组

   

Field[] getDeclaredFields()

    返回所有成员变量对象的数组

   

Field getField(String name)

       返回单个公共成员变量对象,参数name表示成员变量的名字

 

   

Field getDeclaredField(String name)

    返回单个成员变量对象,参数name表示成员变量的名字

   

2.Field类型: 表示一个成员变量类型,每个对象都是一个具体的成员变量

        作用: 获取成员变量的各种信息(修饰符、注解、名称),做各种数据类型的转换.

 

3.Field类用于给成员变量赋值的方法:

        set(Object obj, Object value): 用于给obj对象的,指定成员变量,赋value值

 

4.Field类获取成员变量值的方法:

        get(Object obj): 用于获取obj对象的指定成员变量值

public class Person {
    private String name;
    private String id;
    int age;
    public String sex;
    public String address;

    public String getName() {
        return name;
    }


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

    void print(){
        System.out.println("我是普通print功能,默认修饰");
    }

    private boolean equal(double d1, double d2){
        return d1 == d2;
    }

    public Person(){}

    public Person(String name){
        System.out.println(name);
    }

    Person(String name, int age){
        System.out.println(name + "--" + age);
    }

    private Person(int age){
        System.out.println(age);
    }
}

 

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

public class TestReflect {
    public static void main(String[] args) throws Exception {
        // 1. 获取到Person类型的字节码文件对象
        Class c = Person.class;
        // 2. 获取Person中所有的公共修饰的成员变量
        Field[] fArr = c.getFields();
        for(Field f : fArr){
            System.out.println(f);
        }

        System.out.println("----------------------");

        // 3. Field[] getDeclaredFields()返回所有成员变量对象的数组
        Field[] fArr2 = c.getDeclaredFields();
        for(Field f : fArr2){
            System.out.println(f);
        }

        System.out.println("_______________________________");

        // 4.Field getField(String name)返回单个公共成员变量对象,参数name表示成员变量的名字
        Field f1 = c.getField("sex");
        System.out.println(f1);

        // 6. 通过Field类型中的set方法给指定的成员变量进行赋值
        // set(对象,变量值)
        Constructor con = c.getConstructor();
        Person p = (Person)con.newInstance();
        f1.set(p,"男");
        // get(Object obj): 获取到参数obj对应的指定成员变量的值
        // System.out.println(p.sex);//
        String sex = (String)f1.get(p);
        System.out.println(sex);//// 5. Field getDeclaredField(String name)返回单个成员变量对象,参数name表示成员变量的名字
        Field f2 = c.getDeclaredField("age");
        Field f3 = c.getDeclaredField("name");
        System.out.println(f2);
        System.out.println(f3);
        /* f3.set(p,"张三");
        System.out.println(p.getName());*/
    }
}

 

(二)  反射获取类中的成员方法并执行

1.Class类获取成员方法对象:

   

Method[] getMethods()

    返回所有公共成员方法对象的数组,包括继承的

   

Method[] getDeclaredMethods()

    返回所有成员方法对象的数组,不包括继承的

   

Method getMethod(String methodName, Class<?>...parameterTypes)

    返回单个公共成员方法对象

   

Method getDeclaredMethod(String methodName, Class<?>...parameterTypes)

返回单个成员方法对象

 

   

2.Method类型:

        (1) 表示成员方法的类型,该类型的每个对象,都是一个具体的成员方法

        (2) 成员方法对象具有的功能: 获取成员方法信息,运行方法.

 

3.Method类用于执行方法的功能:

        invoke(Object obj, Object...values):调用obj对象的成员方法,参数是values是运行方法的实际参数,返回值Object类型是方法运行的返回值结果.

 

public class Person {
    private String name;
    private String id;
    int age;
    public String sex;
    public String address;

    public String getName() {
        return name;
    }


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

    void print(){
        System.out.println("我是普通print功能,默认修饰");
    }

    private boolean equal(double d1, double d2){
        return d1 == d2;
    }

    public Person(){}

    public Person(String name){
        System.out.println(name);
    }

    Person(String name, int age){
        System.out.println(name + "--" + age);
    }

    private Person(int age){
        System.out.println(age);
    }
}

 

import java.lang.reflect.Method;

public class ReflectMethod {
    public static void main(String[] args) throws Exception{
        // 1. 获取到Person类型的字节码文件对象
        Class c = Class.forName("com.ujiuye.reflect.Person");
        // 2. 获取Person中所有的公共修饰的方法(包括继承到的)
        Method[] allPubMethod = c.getMethods();
        for(Method m : allPubMethod){
            System.out.println(m);
        }

        System.out.println("-----------------");

        // 3.Method[] getDeclaredMethods()返回所有成员方法对象的数组,不包括继承的
        Method[] allMethod = c.getDeclaredMethods();
        for(Method m : allMethod){
            System.out.println(m);
        }

        System.out.println("--------------------------------");

        // 4. Method getMethod(String methodName, Class<?>...parameterTypes)返回单个公共成员方法对象
        // 第一个参数表示方法名称; 第二个参数是这个方法对应的参数列表  (因为方法可以有重载形式,因此获取某一个方法时必须说明
        // 方法名称和参数列表)
        Method m1 = c.getMethod("setName",String.class);
        System.out.println(m1);
        // invoke(Object obj, Object...values): 第一个参数是一个对象, 第二个参数表示当前方法调用时传递的实际参数
        // 注意: 如果一个类型中有公共修饰的, 空参数构造方法, Class类型中有newInstance() 快速创建出指定类型对象,调用的就是
        // 这个类型的空参数构造方法
        Person p1 = (Person)c.newInstance();
        m1.invoke(p1,"小六六");
        Method m3 = c.getMethod("getName");
        System.out.println(m3);
        String name = (String)m3.invoke(p1);
        System.out.println(name);

        // 5. Method getDeclaredMethod(String methodName, Class<?>...parameterTypes)返回单个成员方法对象
        Method m2 = c.getDeclaredMethod("equal",double.class,double.class);
        System.out.println(m2);
        boolean boo = (Boolean)m2.invoke(p1,3.14,3.14);
        System.out.println(boo);
    }
}

 

(三)  暴力反射

1.通过Class类中:

        getDeclaredXXX方法: 可以获取类中所有声明的成员(属性、方法、构造),私有的成员也可以获取到.但是私有成员进行访问使用时,会因为权限问题导致失败,因此就需要暴力反射解决访问私有的问题

 

2.修改该对象的访问权限:

AccessibleObject类是Field,Method和Constructor对象的基类. 它提供了将反射对象标记为在使用它时抑制默认Java语言访问控制检查的功能.

        setAccessible(boolean flag): true的值表示反射对象应该在使用时抑制Java语言访问检查,false的值表示反映的对象应该强制执行Java语言访问检查.

 

3.一旦设定当前对象可以访问,私有的成员也可以被访问,被修改.

public class Person {
    private String name;
    private String id;
    int age;
    public String sex;
    public String address;

    public String getName() {
        return name;
    }


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

    void print(){
        System.out.println("我是普通print功能,默认修饰");
    }

    private boolean equal(double d1, double d2){
        return d1 == d2;
    }

    public Person(){}

    public Person(String name){
        System.out.println(name);
    }

    Person(String name, int age){
        System.out.println(name + "--" + age);
    }

    private Person(int age){
        System.out.println(age);
    }
}

 

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

public class 暴力反射 {
    public static void main(String[] args) throws Exception {
        // 1. 获取到Person类型字节码文件对象
        Class c = Person.class;
        // 2. 获取Person中的私有构造
        Constructor privateCon = c.getDeclaredConstructor(int.class);
        // 将构造方法进行暴力反射
        privateCon.setAccessible(true);
        Person p = (Person) privateCon.newInstance(18);
        System.out.println(p);
        // 3. 获取到Person中的私有成员变量
        Field f = c.getDeclaredField("name");
        // 将成员变量name进行暴力反射
        f.setAccessible(true);
        f.set(p, "王五");
        // 4. 获取到Person中私有方法
        Method m = c.getDeclaredMethod("equal",double.class, double.class);
        // 将方法进行暴力反射
        m.setAccessible(true);
        boolean boo = (Boolean)m.invoke(p,6.6,6.6);
        System.out.println(boo);
    }
}

 

(四)  泛型擦除案例

   定义出一个ArrayList<String> 集合,泛型中只能存储String类型数据, 利用反射机制,向该集合中成功添加Integer类型数据

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

public class 泛型擦除 {
    public static void main(String[] args) throws Exception{
        // 定义出一个ArrayList<String> 集合,泛型中只能存储String类型数据,
        // 利用反射机制,向该集合中成功添加Integer类型数据
        ArrayList<String> list = new ArrayList<>();
        list.add("abc");

        // 1. 获取到ArrayList类型字节码文件对象
        Class c = Class.forName("java.util.ArrayList");
        // 2. 获取到ArrayList集合中的add方法
        Method m = c.getDeclaredMethod("add",Object.class);
        // 3. 运行m方法
        m.invoke(list,12);
        m.invoke(list,6.89);

        System.out.println(list);
    }
}

 

二. JDK新特性

(一)  接口的新特性

1. 概述:

jdk8之前接口是规则的集合体,方法只有抽象方法。

jdk8版本开始不光有抽象方法同时增加了实体方法。

2. 增加内容:

    jdk8:default默认方法, static静态方法

      jdk9:  private私有方法

 

1.1 默认方法

1. 被关键字 default 修饰的方法就是默认方法,是在jdk8版本才出现的方法,独属于接口所有。

2. 出现的原因:在jdk8版本的时候,需要对一个接口下面的所有的实现类的功能做一个增强,就需要在接口当中去添加方法,如果接口中添加的是抽象方法,下面的实现类就需要强制去重写这些抽象方法,jdk希望在接口当中添加方法,直接就让下面的实现类去使用,不用再次的进行重写,所以添加了使用default做修饰的默认方法,默认方法是可以不被重写的,因为他有方法体。

 

3. 语法格式:

修饰符 default 返回值类型 方法名 (参数列表){方法体}

 

4. 使用规则:

(1) 加上default的,实现类可以不重写,直接调用

(2) 特殊情况1:实现类实现了两个接口,如果有相同的默认方法声明,则强制要求在实现类中,重写这个方法,以指定确定的实现内容

(3) 特殊情况2:在特殊情况1中,如果在实现类重写这个方法的过程中,希望指定其中某一个接口的默认实现,则可以调用”父接口名.super.默认方法名称(实际参数)”

(4) 特殊情况3:实现类实现了继承了一个父类,并且实现了一个接口,并且在父类中和接口中,有相同的方法声明,则“类优先”。即使继承的是一个抽象类,也是使用父类的实现(即强制重写)。

 

5. 产生的影响

(1) 接口中如果也可以定义非抽象方法,那么接口和抽象类的差别就越来越小

(2) java中一个类只能继承一个抽象类,但是可以同时实现多个接口,所以有了默认方法,就会让大量的抽象类变成接口,即弱化了抽象类

 

public interface MyInterface {
    // 1. JDK8之前: 接口中的方法只有抽象方法, 默认修饰符 public abstract
    void show();
    // 2. 在JDK8版本: 可以在接口中添加默认方法default, 还有静态方法static
    /*
         接口中添加默认方法目的: 为了后期便于接口的维护,默认方法可以直接被实现类继承使用,不强制重写
     */
    public default int getSum(int x, int y){
        return x + y;
    }
}

 

public class MyInterfaceImpl extends Fu implements MyInterface,InterfaceOther{
    @Override
    public void show() {
        System.out.println("重写了父接口中的抽象方法");
    }

    @Override
    public void fun() {

    }

    // 1. 如果一个类同时实现多个接口, 并且父接口中定义出相同的默认方法声明,导致实现类继承的默认方法冲突
    // ,于是要求实现类必须重写重复的默认方法
    /*@Override
    public int getSum(int x, int y){
        System.out.println("我是重写的默认方法");
        // 2. 如果实现类想要调用父接口中的默认方法  父接口名.super.默认方法(实际参数);
        return InterfaceOther.super.getSum(x,y);
        // return x * y;
    }*/
}

 

public class Fu {
    public int getSum(int x, int y){
        return (x + y) * 10;
    }
}

 

public interface InterfaceOther {
    void fun();
    public default int getSum(int x, int y){
        return 2 * x * y;
    }
}

 

public class TestInterface {
    public static void main(String[] args) {
        // 1. 实现类可以直接继承使用从父接口中继承到的默认方法
        MyInterfaceImpl mil = new MyInterfaceImpl();
        mil.show();
        System.out.println(mil.getSum(4,5));
    }
}

 

1.2 静态方法

1、接口的静态方法可以定义方法体内容

2、static不能和abstract共存

3、外界只能通过接口名称.静态方法来访问接口静态方法

4、实现类中不会继承接口中的静态方法,原因:如果一个类同时实现了两个接口,具有相同的静态方法名,继承之后不知道应该以哪个为准, 而静态方法又不能重写,因此矛盾无法解决

 

public interface MyInterface {
    // 3. JDK8版本中: 可以添加静态方法static
    //  1) 静态方法只属于接口本身, 可以用接口名.直接调用
    //  2) 接口中的静态方法不给实现类继承使用
    public static void print(int n){
      for(int i = 1; i <= n; i++){
          System.out.println(i);
      }
    }
}

 

public class TestInterface {
    public static void main(String[] args) {
        // 2. 接口中的静态方法不能给实现类继承使用
        MyInterface.print(5);
        //MyInterfaceImpl.print(6);
    }
}

 

1.3 私有方法

1. 概述: 私有方法是jdk9版本增加的一个实体方法,主要是用来进一步封装代码,提升相关代码安全性的手段。私有化之后方法不能被实现类直接调用使用或重写修改,只能提供给接口的静态方法和默认方法使用。

2. 使用:

  (1) 普通私有方法:只能提供给默认方法调用使用

  (2) 静态私有方法:默认方法和静态方法都可以调用

    静态方法只能使用静态的私有方法,不能使用普通的私有方法

public interface MyInterface {

    public default void fun(){
        // fun中有很多逻辑代码,实现类就可以直接将这些代码继承,使用
        // 于是为了安全度考虑, 可以将fun中的部分代码逻辑封装在一个私有方法中
        test();
        testShow();
    }

    public static void show(){
        testShow();
    }

    // 1. 普通私有方法只能在当前接口中的默认方法中调用
    private void test(){
        System.out.println("你好");
    }

    // 2. 静态私有方法即可以在默认方法中调用, 也可以在静态方法中进行调用
    private static void testShow(){
        System.out.println("我是为了静态方法代码安全性考虑设计");
    }
}

 

posted @ 2022-04-02 20:10  晴天般的微  阅读(91)  评论(0)    收藏  举报