• 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("我是为了静态方法代码安全性考虑设计");
        }
    }
  • 相关阅读:
    HDU 4492 Mystery (水题)
    UVA 10480 Sabotage (最大流)
    POJ 2446 Chessboard (二分匹配)
    VS2008下用MFC 的MSComm控件编写串口程序
    退役了~~~
    STL Algorithms 之 unique
    cocos2d中CCCallFuncND传参数的注意事项
    好太太晾衣架市场价格表(仅供参考)
    Linux VNC黑屏(转)
    C++ TinyXml操作(含源码下载)
  • 原文地址:https://www.cnblogs.com/lw1095950124/p/16093792.html
Copyright © 2020-2023  润新知