Java基础语法-反射

一、Reflection(反射)概念

1.Reflection(反射)是Java被视为动态语言的关键,反射机制允许程序在执行期借助Reflection API获得任何类的内部信息,并能直接操作任意对象的内部属性及方法。 Class c = Class.forName("java.lang.String");

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

正常方式: 引入需要的 "包类"名称---->通过new实例化----->获取实例化对象。

反射方式:实例化对象---->getClass方法----->得到完整的"包类"名称。

二、Reflection(反射)机制提供的功能

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

2.在运行时构造任意一个类的对象。

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

4.在运行时获取泛型信息。

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

6.在运行时处理注解。

7.生产动态代理。

三、Class类

某个类的属性、方法和构造器、某个类到底实现了哪些接口。对于每个类而言,JRE都为其保留一个不变的Class类型的对象。一个Class对象包含了特定某个结构(class/interface/enum/annotation/primitive type/void/[])的有关信息。

一个类在内存中只存在一个Class对象,一个类被加载后,类的整个结构都会被封装到Class对象中。

1.Class本身也是一个类。

2.Class对象只能由系统建立对象。

3.一个加载的类在JVM中只会有一个Class实例。

4.一个Class对象对应的是一个加载到JVM中的一个.class文件。

5.每个类的实例都会记得自己是由哪个Class实例所生产。

6.通过Class可以完整地得到一个类中的所有被加载的结构。

7.Class类是Reflection的根源,针对任何你想动态加载、运行的类,唯有先获得相应Class对象。

1、常用方法:

1.static Class  forName(String className):返回指定类名name的Class对象。

2.String  getName():返回此Class对象所表示的实体(类、接口、数组类或void)的名称。

3. T  newInstance():调用缺省构造函数,返回Class对象的一个实例。

4. Class  getSuperclass():返回当前Class对象的父类的Class对象。

5.Class[]  getInterfaces():获取当前Class对象的接口。

6.ClassLoader  getClassLoader():返回该类的类加载器。

7.Constructor[]  getConstructors():返回一个包含某些Constructor对象的数组。

8.Method  getMethod(String name, Class... parameterTypes):返回一个Method对象。

9.Field[]  getDeclaredFields():返回Filed对象的一个数组。

2、获取Class类的实例:

1.已知具体的类,通过类的class属性获取,该方法最为安全可靠,程序性能最高。

Class clazz = Person.class;。

2.已知某个类的实例,调用该类实例的getClass()方法获取Class对象。

Class clazz = person.getClass();。

3.已知一个类的全类名,且该类在类路径下,可通过Class类的静态方法forName()获取。

Class clazz = Class.forName("com.Person");。

4.内置基本数据类型可以直接用类名.Type(Integer.TYPE)。

5.利用ClassLoader获取。

代码示例:

public class ClassDemo {
    public static void main(String[] args) throws ClassNotFoundException {
        Person person = new Student();
        System.out.println("这个人是:"+person.getName());
        //方法一: 通过对象获取
        Class c1 = person.getClass();
        System.out.println(c1.hashCode());//返回结果:933699219
        //方式二: 通过forName获得
        Class c2 = Class.forName("com.ywl.controller.Student");
        System.out.println(c2.hashCode());//返回结果:933699219
        //方式三: 通过类名.class获取
        Class c3 = Student.class;
        System.out.println(c3.hashCode());//返回结果:933699219
        //方式四: 基本内置类型的包装类都有一个Type属性
        Class c4 = Integer.TYPE;
        System.out.println(c4);//返回结果:int
        //获取父类类型
        Class superclass = c1.getSuperclass();
        System.out.println(superclass);//返回结果:class com.ywl.controller.Person

    }

}
class Person{

    private String name;

    public Person() {

    }

    public String getName() {
        return name;
    }

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

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                '}';
    }
}
class Student extends Person{
    public Student() {
        this.setName("学生");
    }
}
class Teacher extends Person{
    public Teacher() {
        this.setName("老师");
    }
}

3、动态创建对象执行方法:

代码示例:

public class ClassDemo {
    public static void main(String[] args) throws ClassNotFoundException {
        Class userClass = User.class;
        //获取类名
        System.out.println("包名+类名:"+userClass.getName());
        System.out.println("类名:"+userClass.getSimpleName());
        //获取类的所有属性(userClass.getFields()获取public属性)
        Field[] fields = userClass.getDeclaredFields();
        for (Field field : fields) {
            System.out.println("getDeclaredFields:"+field);
        }
        //获取类的所有方法(userClass.getMethods()获取public方法)
        Method[] declaredMethods = userClass.getDeclaredMethods();
        for (Method declaredMethod : declaredMethods) {
            System.out.println("getDeclaredMethods:"+declaredMethod);
        }
        //获取类所有的构造器
        Constructor[] declaredConstructors = userClass.getDeclaredConstructors();
        for (Constructor declaredConstructor : declaredConstructors) {
            System.out.println("getDeclaredConstructors:"+declaredConstructor);
        }

    }
}
class User{

    private String name;

    public User() {

    }

    public String getName() {
        return name;
    }

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


}

打印结果:

包名+类名:com.ywl.controller.User
类名:User
getDeclaredFields:private java.lang.String com.ywl.controller.User.name
getDeclaredMethods:public java.lang.String com.ywl.controller.User.getName()
getDeclaredMethods:public void com.ywl.controller.User.setName(java.lang.String)
getDeclaredConstructors : public com.ywl.controller.User()

4、创建类的对象:调用Class对象的newInstance()方法。

1.类必须有一个无参数的构造器。

2.类的构造器的访问权限需要足够。

3.调用指定的方法:Object invoke(Object obj,Object..... args)

(1).Object对应原方法的返回值,若原方法无返回值,此时返回null。

(2).若原方法为静态方法,此时形参Object obj可为null。

(3).若原方法形参列表为空,则Object[] args为null。

(4).若原方法声明为private,则需要在此调用此invoke()方法前,显示调用方法对象的setAccessible(true)方法,才可访问                 private的方法。
4.setAccessible方法:

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

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

(3).参数值为true则指示反射的对象在使用时应该取消Java语言访问检查。使得原本无法访问的私有成员也可以访问。

(4).参数值为false则指示反射的对象应该实施Java语言访问检查。

代码示例:

public class ClassDemo {
    public static void main(String[] args) throws Exception {
        Class userClass = Class.forName("com.ywl.controller.User");
        //通过反射调用普通方法
        User user = (User)userClass.newInstance();
        //通过反射获取一个方法。
        Method setName = userClass.getDeclaredMethod("setName", String.class);
        setName.invoke(user,"张三");
        System.out.println(user.getName());

        //通过反射操作属性
        Field name = userClass.getDeclaredField("name");

        name.setAccessible(true);//关闭程序的安全检测才可以操作private属性
        name.set(user,"李四");
        System.out.println(user.getName());

    }
}
class User{

    private String name;

    public User() {
    }

    public String getName() {
        return name;
    }

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

}

打印结果:

张三
李四

四、类的加载与ClassLoader的理解

1.加载:将class文件字节码内容加载到内存中,并将这些静态数据转换成方法区的运行时数据结构,然后生成一个代表这个类的java.lang.Class对象。

2.链接:将Java类的二进制代码合并到JVM的运行状态之中的过程。

(1).验证:确保加载的类信息符合JVM规范,没有安全方面的问题。

(2).准备:正式为类变量(static)分配内存并设置类变量默认初始值的阶段,这些内存都将在方法区中进行分配。

(3).解析:虚拟机常量池内的符号引用(常量名)替换为直接引用(地址)的过程。

3.初始化:

1.执行类构造器()方法的过程。类构造器()方法是由编译期自动收集类中所有类变量的赋值动作和静态代码块中的语句合并产生的。(类构造器是构造类信息的,不是构造该类对象的构造器)。

2.当初始化一个类的时候,如果发现其父类还没有进行初始化,则需要先触发其父类的初始化。

3.虚拟机会保证一个类的()方法在多线程环境中被正确加锁和同步。

五、类的加载器

类加载器的作用是把类(class)装载进内存的。JVM规范定义了如下类型的类加载器:

1.引导类加载器:用C++编写的,是JVM自带的类加载器,负责Java平台核心库,用来装载核心类库,该加载器无法直接获取。

2.扩展类加载器:负责jre/lib/ext目录下的jar包或-D java.ext.dirs指定目录下的jar包装入工作库。

3.系统类加载器:负责java -classpath或-D java.class.path所指的目录下的类与jar包装入工作,是最常用的加载器。

代码示例:

public class ClassDemo {
    public static void main(String[] args) throws ClassNotFoundException {
        //获取系统类的加载器
        ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();
        System.out.println("获取系统类的加载器:"+systemClassLoader);
        //获取系统类加载器的父类加载器-->扩展类加载器
        ClassLoader parent = systemClassLoader.getParent();
        System.out.println("获取系统类加载器的父类加载器:"+parent);
        //获取扩展类加载器的父类加载器-->根加载器(C/C++)
        ClassLoader parent1 = parent.getParent();
        System.out.println("获取扩展类加载器的父类加载器:"+parent1);
        //获取当前类的类加载器
        ClassLoader classLoader = Class.forName("com.ywl.controller.ClassDemo").getClassLoader();
        System.out.println("获取当前类的类加载器:"+classLoader);
        //获取JDK内部类加载器
        classLoader = Class.forName("java.lang.Object").getClassLoader();
        System.out.println("获取JDK内部类加载器:"+classLoader);
    }
}

打印结果:

获取系统类的加载器:sun.misc.Launcher$AppClassLoader@2dda6444
获取系统类加载器的父类加载器:sun.misc.Launcher$ExtClassLoader@37a71e93
获取扩展类加载器的父类加载器:null
获取当前类的类加载器:sun.misc.Launcher$AppClassLoader@2dda6444
获取JDK内部类加载器:null

 

 

 

 

 

你可能感兴趣的:(Java基础)