反射(适合小白)

1.一个需求引出反射配置文件中的指定信息,创建Cat对象并调用方法

2.这样的需求在学习框架中特别多,即通过外部文件配置,在不修改源码的情况下来控制程序,也符合设计模式的ocp原则(开闭原则:不修改源码,扩容功能)

例题

package com.xx;

import sun.awt.HeadlessToolkit;
public class Cat {
    private String name="招财猫";
    public int age =0;//
    public void Cat(){}
    public void Cat(String name){
        this.name=name;
    }

            public void hi(){
//                System.out.println("hi"+name);
            }

            public void eat(){
                System.out.println("小猫在吃鱼");
            }
}

re.properties

classfullpath=com.xx.Cat
method =cry

 反射问题的引入

package com.xx.reflection.question;

import com.xx.Cat;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Properties;

/**
 * 反射问题的引入
 */
public class ReflectionQuestion {
    public static void main(String[] args) throws IOException, ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException {
        //根据配置文件 re.properties 指定信息,创建Cat对象并调用方法hi

        //传统的方式 new 对象 调用方法
//        Cat cat =new Cat();
//        cat.hi();

        //1.使用properties类,可以读写配置文件
        Properties properties = new Properties();
        properties.load(new FileInputStream("D://ideaProjects/charpter22//src//re.properties"));
        String classfullpath= properties.get("classfullpath").toString();
        String methodName = properties.get("method").toString();
        String methodName1 = properties.get("method1").toString();
        System.out.println("classfullpath="+classfullpath);
        System.out.println("method="+methodName);
        System.out.println("method1="+methodName1);

        //2.创建对象 传统的方法行不通 使用反射机制
//        new classfullpath()

        //3.使用反射机制来解决
        //1)加载类 返回Class类型对象
        Class aClass = Class.forName(classfullpath);

        //2)通过aClass得到你加载的类 com.xx.Cat的对象实例
        Cat o=(Cat)aClass.newInstance();
        System.out.println(o.getClass());

        //3)通过aClass得到你加载的类com.xx.Cat的methodName的方法对象
              //在反射中,可以把方法视为对象
        Method method1 = aClass.getMethod(methodName);
        Method method2 = aClass.getMethod(methodName1);

        //4)通过method1调用方法 ,通过方法对象来实现调用方法
        System.out.println("-------------------");
        method1.invoke(o);//按照传统方法是=>对象.方法  反射机制 =>方法.invoke(对象)
        method2.invoke(o);//按照传统方法是=>对象.方法  反射机制 =>方法.invoke(对象)

    }
}

 反射机制:

1.反射机制允许程序在执行期借助于ReflectionAPI取得任何类的内部信息(比如成员变量,构造器,成员方法等等)并能操作对象的属性及方法,反射在设计模式和框架底层都会用到

2.加载完类之后,在堆中就产生了一个Class类型的对象(一个类只有一个Class对象),这个对象包含了类的完整结构信息,通过这个对象得到类的结构。这个对象就像一面镜子,透过这个镜子看到类的结构,所以,形象的称之为:反射

反射机制原理示意图

反射(适合小白)_第1张图片

 Java反射机制可以完成

1.在运行时判断任意一个对象所属的类

2.在运行时构造任意一个类

3.在运行时得到任意一个类所具有的成员变量和方法

4.在运行时调用任意一个对象的成员变量和方法

5.生成动态代理

反射相关的主要类

1.java.lang.Class:代表一个类,Class对象表示某个类加载后在堆中的对象

2.java.lang.reflect.Method:代表类的方法,Method对象表示某个类 的方法

3.java,lang.reflect.Field:代表类的成员变量,Filed对象表示某个类的成员变量

4.java.lang.reflect.Constructor:代表类的构造方法,Constructor对象表示构造器

package com.xx.reflection.question;

import com.xx.Cat;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Properties;

/**
 * 反射问题的引入
 */
public class Reflection01 {
    public static void main(String[] args) throws Exception {

        Properties properties = new Properties();
        properties.load(new FileInputStream("D://ideaProjects/charpter22//src//re.properties"));
        String classfullpath= properties.get("classfullpath").toString();
        String methodName = properties.get("method").toString();


        //2.创建对象 传统的方法行不通 使用反射机制
//        new classfullpath()

        //3.使用反射机制来解决
        //1)加载类 返回Class类型对象
        Class aClass = Class.forName(classfullpath);

        //2)通过aClass得到你加载的类 com.xx.Cat的对象实例
        Cat o=(Cat)aClass.newInstance();
        System.out.println(o.getClass());

        //3)通过aClass得到你加载的类com.xx.Cat的methodName的方法对象
        //在反射中,可以把方法视为对象
        Method method1 = aClass.getMethod(methodName);

        //4)通过method1调用方法 ,通过方法对象来实现调用方法
        System.out.println("-------------------");
        method1.invoke(o);//按照传统方法是=>对象.方法  反射机制 =>方法.invoke(对象)

        //java.lang.reflect.Field;代表类的成员变量
        //得到name字段
        //getFiled不能得到私有的属性
        Field nameFiled = aClass.getField("age");
        System.out.println(nameFiled.get(o));//传统方法 对象.成员变量  反射:成员变量对象.get(对象)

        //java.lang.reflect.Constructor:代表类的构造方法 ,Constructor对象代表构造器
        Constructor constructor = aClass.getConstructor();//()中可以指定构造器参数类型,没有写的返回无参构造器
        System.out.println(constructor);
//
        Constructor constructor2 = aClass.getConstructor(String.class);//这里来事传入的String.class就是String的对象
        System.out.println(constructor2);


    }
}

 反射的优点和缺点

1.优点:可以动态的创建和使用对象使用灵活,没有反射机制,框架技术就会失去底层支撑

2.缺点:使用反射基本上是解释执行,对执行速度有影响

 反射调用优化

1.Method和Field,Constructor对象都有setAccessible()方法

2.setAccessible作用是启动和禁用访问安全检查的开关

3.参数值为true表示  反射的对象在使用时取消访问检查,提高反射的效率。参数值为false

    则表示反射的对象执行访问检查

 Class类

1.Class类也是类,因此也继承Object类

2.Class类对象不是new出来的,而是系统创建的

3.对于某个类的Class类对象,在内存中只有一份,因为类只加载一次

4.每个类的实例都会记得自己是由哪个 Class实例所生成

5.通过Class可以完整的得到一个类的完整结构,通过一系列API

6.Class对象是存放在堆的

7.类的字节码二进制数据,是放在方法区的,有点地方称为类的元数据(包括 方法代码  变量名,方法名,访问权限等等)

 反射(适合小白)_第2张图片

package com.xx.reflection.question.Class;

import com.xx.Cat;

public class Class01 {
    public static void main(String[] args) throws ClassNotFoundException {


    //看看Class类图
    //1.Class也是类 因此也继承Object类
    //Class
    //2.Class类对象 不是New出来的,而是系统创建的
    //1)传统New 对象
        //ClassLoader类,仍然是通过ClassLoader类加载Cat类的Class对象
//    public Class loadClass(String name)throws ClassNotFoundException{
//        return loadClass(name,false);
//    }
//        Cat cat = new Cat();
        //2)反射方式
        Class aClass = Class.forName("com.xx.Cat");
//    ClassLoader类,仍然是通过ClassLoader类加载Cat类的Class对象
//    public Class loadClass(String name)throws ClassNotFoundException{
//        return loadClass(name,false);
//    }

  //3. 对于某个类的Class类对象,在内存中只有一份,因为类只加载一次
        Class cls2 = Class.forName("com.xx.Cat");
        System.out.println(cls1.hashCode());
        System.out.println(cls2.hashCode());
        Class cls3 = Class.forName("com.xx.Dog");
        System.out.println(cls3.hashCode());

    }
}

Class类的常用方法

package com.xx.reflection.question.Class;
import com.xx.Car;
import java.lang.reflect.Field;

/**
 * 演示Class类的常用方法
 */
public class Class02 {
    public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchFieldException {

        String classAllPath="com.xx.Car";
        //2.获取到Car类 对应的Class对象
        //表示不确定的Java类型
        Class cls = Class.forName(classAllPath);
        //2.输出cls
        System.out.println(cls);//显示cls对象 是哪个类的Class对象 com.xx.Car
        System.out.println(cls.getClass());//输出运行类型 java.lang.Class
        //3.得到包名
        System.out.println(cls.getPackage().getName());//包名
        //4.得到全类名
        System.out.println(cls.getName());
        //5.通过我们的cls来创建一个对象实例
        Car car= (Car)cls.newInstance();
        System.out.println(car);
        //6.通过反射获取属性brand
        Field brand = cls.getField("brand");
        //不能获取私有属性
        System.out.println(brand.get(car));//aodi
        //7.通过反射给属性赋值
        brand.set(car,"奔驰");
        System.out.println(brand.get(car));

        System.out.println("所有的字段属性");
        //8.我希望可以得到所有的属性
        Field[] fields = cls.getFields();
        for (Field f:fields) {
            System.out.println(f.getName());//名称
        }
    }
}

获取Class对象

1.前提:已知一个类的全类名,且该类的类路径下,可通过Class的静态方法forName()获取,可能抛出ClassNotFoundException

实例:Class clazz = Class.forName(java.lang.Cat");

应用场景:多用于配置文件,读取类的全路径,加载类

2.前提:若已知具体的类,通过类的class获取,该方式最为安全可靠,程序性能最高

实例:Class cla2 = Cat.class

应用场景:多用于参数传递,比如通过反射得到对应构造对象

3.前提:已知某个类的实例,调用该实例的getClass()方法获取Class对象,

实例:Class clazz = 对象.getClass()

应用场景:通过创建好的对象,获取Class对象

4.其他方式

ClassLoader cl =  对象.getClass().getClassLoader();

Class clazz4 = cl.loadClass("类的全类名")

5.基本数据(int,char,boolean,float,double,byte,long,short)按如下方式得到Class类对象

Class cls =基本数据类型.class

6.基本数据类型对应的包装类,可以通过.type得到Class对象

Class cls = 包装类.Type

package com.xx.reflection.question.Class;

import com.sun.scenario.effect.impl.sw.sse.SSEBlend_SRC_OUTPeer;
import com.xx.Car;

/**
 * 获取Class类对象的六中方法
 */
public class getClass {
    public static void main(String[] args) throws ClassNotFoundException {
        //1.多用于配置文件 读取类的全路径,加载类
        //Class.fotName
        String classAllPath="com.xx.Car";
        Class cls1 = Class.forName(classAllPath);
        System.out.println(cls1);


        //2 多用于参数传递
        // 类名.class
        Class cls2= Car.class;
        System.out.println(cls2);

        //通过创建好的对象 获取Class对象
        //Class clazz =对象.getClass()//运行类型
        Car car =new Car();
        Class cls3=car.getClass();
        System.out.println(cls3);

        //4. 通过类加载器来获取类的Class对象
        //1)先得到类加载器
        ClassLoader classLoader = car.getClass().getClassLoader();
        //2)通过类加载器得到Class对象
        Class cls4 = classLoader.loadClass(classAllPath);
        System.out.println(cls4);


        //cls1,cls2,cls3,cls4都是同一个对象那个
        System.out.println(cls1.hashCode());
        System.out.println(cls2.hashCode());
        System.out.println(cls3.hashCode());
        System.out.println(cls4.hashCode());

        //5.基本数据 (int ,char,boolean,float,double,long,short)按如下方式得到Class类对象
        Class integerClass = int.class;
        Class characterClass = char.class;
        Class booleanClass = boolean.class;
        System.out.println(integerClass);
        System.out.println(characterClass);
        System.out.println(booleanClass);

        //6.基本数据类型的包装类 可以通过.Type得到Class对象
        Class type = Integer.TYPE;
        Class type1 = Character.TYPE;
        System.out.println(type);
        System.out.println(type1);

        System.out.println(integerClass.hashCode());
        System.out.println(type.hashCode());


    }
}

那些类型有Class对象

1.外部类,成员内部类,静态内部类,局部内部类,匿名内部类

2.interface:接口

3.数组

4.enum:枚举

5.annotation:注解

6.基本数据类型

7.void

import java.io.Serializable;
/**
 * 演示哪些类型有Class对象
 */
public class AllTypeClass {
    public static void main(String[] args) {
        Class cls1 = String.class;//外部类
        Class cls2 = Serializable.class;//接口
        Class cls3 = Integer[].class;//数组
        Class cls4 = float[][].class;//二维数组
        Class cls5 = Deprecated.class;//注解
        //枚举
        Class cls6 = Thread.State.class;
        Class cls7 = long.class;//基本数据类型
        Class cls8 = void.class;//void数据类型
        Class cls9 = Class.class;//

        System.out.println(cls1);
        System.out.println(cls2);
        System.out.println(cls3);
        System.out.println(cls4);
        System.out.println(cls5);
        System.out.println(cls6);
        System.out.println(cls7);
        System.out.println(cls8);
        System.out.println(cls9);
    }
}

类加载

反射机制是java实现动态语言的关键,也就是通过反射实现类动态加载

1.静态加载:编译时加载相关的类,如果没有则报错,依赖性太强

2.动态加载:运行时加载需要的类,如果运行时不用该类,即是不存在该类,则不报错,

   降低了依赖性

3.类的加载时机

1)当创建对象时 (new)//静态加载

2)当子类被加载时,父类也加载 //静态加载

3)调用类中的静态成员时//静态加载

4)通过反射//动态加载

反射(适合小白)_第3张图片 加载阶段

 连接阶段

1.目的:是为了确保Class文件到字节流中的信息符合当前虚拟性的要求,并且不会虚拟性

              自身的安全

2.包含:文件格式验证,元数据验证,字节码验证和符号引用验证

3.可以考虑使用 xcerify:none参数来关闭大部分的类验证措施,缩短虚拟机类加载的时间

 连接准备

JVM会在该阶段对静态变量,分配内存并默认初始化。这些变量所使用的内存都会在方法   进行分配

 Intialization

1.到初始化阶段,才开始真正执行类中定义的Java程序代码,此阶段是执行{}方法的过程

2.()方法是由编译器按语句在源文件中出现的顺序,依次自动收集类中的所有静态变量的赋值动作和静态代码块中的语句,并进行合并

3.虚拟机会保证一个类的()方法在多线程环境中被正确的加锁,同步,如果多个线程同时去初始化一个类,那么只会有一个线程去执行这个类()方法,其他线层都需要阻塞等待,直到活动线程执行()方法完毕

 例题

package com.xx.reflection.question.ClassLoad;

/**
 * 初始化阶段
 */
public class classLoad02 {
    public static void main(String[] args) {
 
        //1. 加载B类,并生成 B的class对象
        //2. 链接 num = 0
        //3. 初始化阶段
        //    依次自动收集类中的所有静态变量的赋值动作和静态代码块中的语句,并合并
        /*
                clinit() {
                    System.out.println("B 静态代码块被执行");
                    //num = 300;
                    num = 100;
                }
                合并: num = 100
         */
        //new B();//类加载
        //System.out.println(B.num);//100, 如果直接使用类的静态属性,也会导致类的加载
        //看看加载类的时候,是有同步机制控制
        /*
        protected Class loadClass(String name, boolean resolve)
        throws ClassNotFoundException
        {
            //正因为有这个机制,才能保证某个类在内存中, 只有一份Class对象
            synchronized (getClassLoadingLock(name)) {
            //....
            }
            }
         */
    }
}
class B{
    static {
        System.out.println("B的静态代码块被执行");
        num = 300;
    }
    static int num =100;
    public B(){
        System.out.println("B()构造器被执行");
    }
}

通过反射获取类的结构信息

第一组:java.lang.Class类

1.getName:获取全类名

2.getSimpleName:获取简单类名

3.getFields:获取所有public修饰的属性,包含本类以及父类的

4.getDeclaredFields:获取本类中的所有方法

5.getMethods:获取所有Public修饰的方法,包含本类以及父类的

6.getDeclaredMethods:获取本类中所有方法

7.getConstructors:获取本类所有public修饰的 构造器

8.getDeclaredConstructors:获取本类中所有构造器

9.getPackage:以Packag形式返回包信息

10.getSuperClass:以Class形式返回父类信息 

11.getInterfaces:以Class[]形式返回接口信息

12.getAnnotation:以Annotation[]形式返回注解信息

第二组:java.lang.reflect.Field类

1.getModifiers:以int形式

2.getType:以Class形式返回类型

3.getName:返回属性名

第三组:java.lang.reflect.Method类

1.getModifies:以int形式返回修饰符

[默认修饰符 0,private 是 1,protected 是 4,static 是 8 ,final是16]

2.getReturnType:以Class形式获取返回类型

3.getName:返回方法名

4.getParameterTypes:以Class[]返回参数类型数组

第四组:java.lang.reflect.Constructor类

1.getModifiers:以Int形式返回修饰符

2.getName:返回构造器(全类名)

3.getParameterTypes:以Class[]返回参数类型数组

package com.xx.reflection.question;

import org.junit.Test;

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

/**
 * 如何通过反射获取类的结构信息
 */
public class ReflectionUtils {
    public static void main(String[] args) throws ClassNotFoundException {
        //规定 说明: 默认修饰符 是0 , public  是1 ,private 是 2 ,protected 是 4 , static 是 8 ,final 是 16
        Class personCls = Class.forName("com.xx.reflection.question.Person");
        //getDeclaredFields:获取本类中的所有属性
        Field[] declaredFields = personCls.getDeclaredFields();
        for (Field declaredField : declaredFields) {
            System.out.println("本类所有的属性="+declaredField.getName()+
                    "对应属性的修饰符值="+declaredField.getModifiers()+
                    "对应属性的类型="+ declaredField.getType());
        }
        System.out.println("===================");
        //getMethods:获取所有Public修饰的方法,包含本类已经父类的
        Method[] methods = personCls.getMethods();
        for (Method method : methods) {
            System.out.println("本类及父类的公共方法="+method.getName()+
                    "该方法的修饰符值="+method.getModifiers()+
                    "该方法返回类型="+method.getReturnType());
            //输出当前这个方法的形参数组情况
            Class[] parameterTypes = method.getParameterTypes();
            for (Class parameterType : parameterTypes) {
                System.out.println("该方法的形参类型=" + parameterType);
            }
        }
        System.out.println("==================");
        //getDeclaredConstructors:获取本类中所有的构造器
        Constructor[] declaredConstructors = personCls.getDeclaredConstructors();
        for (Constructor declaredConstructor : declaredConstructors) {
            System.out.println("本类中所有的构造器= "+declaredConstructor.getName());

            Class[] parameterTypes = declaredConstructor.getParameterTypes();
            for (Class parameterType : parameterTypes) {
                System.out.println("该构造器的形参类型"+parameterType);
            }
        }
    }

    public void api_02(){

    }
    @Test
//第一组方法api
    public void api_01() throws ClassNotFoundException {
//得到Class对象
        Class personCls = Class.forName("com.xx.reflection.question.Person");
        //getName:获取全类名
        System.out.println(personCls.getName());//
        //getSimpleName获取简单类名
        System.out.println(personCls.getSimpleName());
        //getFields:获取所有Public修饰的属性 包含本类以及父类的
        Field[] fields = personCls.getFields();
        for (Field field : fields) {//增强for循环
            System.out.println("本类以及父类的属性="+field.getName());
        }
        //getDeclaredFields:获取本类中的所有属性
        Field[] declaredFields = personCls.getDeclaredFields();
        for (Field declaredField : declaredFields) {
            System.out.println("本类所有的属性="+declaredField);
        }
        //getMethods:获取所有Public修饰的方法,包含本类已经父类的
        Method[] methods = personCls.getMethods();
        for (Method method : methods) {
            System.out.println("本类及父类的公共方法="+method.getName());
        }
        //getDeclaredMethod:获取本类中所有的方法
        Method[] declaredMethods = personCls.getDeclaredMethods();
        for (Method declaredMethod : declaredMethods) {
            System.out.println("本类及父类的所有方法="+declaredMethod.getName());
        }
        //getConstructors:获取所有public 修饰的构造器,只有本类的
        Constructor[] constructors = personCls.getConstructors();
        for (Constructor constructor : constructors) {
            System.out.println("本类公共构造器="+constructor.getName());
        }
        //getDeclaredConstructors:获取本类中所有的构造器
        Constructor[] declaredConstructors = personCls.getDeclaredConstructors();
        for (Constructor declaredConstructor : declaredConstructors) {
            System.out.println("本类中所有的构造器= "+declaredConstructor.getName());
        }
        //getPacket:以Package形式返回包信息
        System.out.println(personCls.getPackage());
        //getSuperClass:以Class形式返回父类信息
        System.out.println("父类的class对象"+personCls.getSuperclass());
        //getInterfaces: 以Class[]形式返回接口信息
        Class[] interfaces = personCls.getInterfaces();
        for (Class anInterface : interfaces) {
            System.out.println("接口信息 ="+anInterface.getName());
        }
        //getAnnotations:以Annotation[]形式返回注解信息
        Annotation[] annotations = personCls.getAnnotations();
        for (Annotation annotation : annotations) {
            System.out.println("注解信息="+annotation);
        }

    }
}

class A{
    public String hobby;
    private void hi()
    {

    }
    public A(){}
}
interface IA{

}
interface IB{

}
@Deprecated
class Person extends A implements IA,IB{

    public Person(){}
    public Person(String name){
    }

    private Person(int age ,String name){

    }
    //属性
    public String name;
    protected static int age;
    String job;
    private double sal;
    //方法
    public void m1( String name ,int age,double sal){

    }
    protected void m2(){

    }
    void m3(){

    }
    private void m4(){

    }
}

你可能感兴趣的:(java,开发语言)