反射学习笔记

1、动态与静态语言

  1. 动态语言

    在运行时可以改变其结构的语言:例如新的函数、对象、甚至代码可以被引进,已有的函数可以被删除或是发生改变。通俗的说就是在运行时代码可以根据某些条件改变自身结构。主要有Js 、python、PHP、C#、等。

  2. 静态语言

    运行时结构不可变的语言,有Java、C、C++。

Java可以称之为“准动态语言”。即Java有一定的动态性,可以利用反射机制获得类似动态语言的特性。Java的动态性让编程的时候更加灵活。

2、Class类获取方法

反射后可以得到的信息:某个类的属性、方法和构造器、实现了哪些接口。对于每个类,JRE都为其保留一个不变的Class类型的对象。一个Class队形包含了特定某个结构的有关信息

  1. Class本身是一个类
  2. Class对象只能由系统建立对象
  3. 一个加载的类在JVM中只会有一个Class实例
  4. 一个Class对象对应的是一个加载到JVM中的一个.class文件
  5. 每个类的实例都会记得自己是由那个Class实例所生成
  6. 通过Class可以完整地得到一个类中的所有被加载的结构

Object的getClass()方法

@IntrinsicCandidate
public final native Class<?> getClass();
//返回此Object的运行时类。返回的Class对象是被表示类的static synchronized方法锁定的对象。

有四种方式获取Class对象

public class User {
    private int id;
    private String name;
    public static void main(String[] args) throws ClassNotFoundException {
        //通过对象名调用forName,可能抛出ClassNotFoundException
        Class c1 = Class.forName("User");
        //类名。calss
        Class c2 = User.class;
        //对象.getClass()
        Class c3 = new User().getClass();
        //包装类.Type属性,返回的是基本类型Class
        Class c4 = Integer.TYPE;
    }
}

class类、interface接口、数组、枚举、注解annotation、基本数据类型、void均有其对应的class对象

 Class c1 = Object.class;  //class java.lang.Object
 Class c2 = Runnable.class;//interface java.lang.Runnable
//对于数组,类型和维度一致则对应同一Class对象
 Class c3 = String[].class;//class [Ljava.lang.String;
 Class c4 = int[][].class;//class [[I
 Class<Override> c5 = Override.class;//interface java.lang.Override
 Class<ElementType> c6 = ElementType.class;//class java.lang.annotation.ElementType
 Class<Integer> c7 = int.class;//int
 Class<Void> c8 = void.class;//void
 Class<Class> c9 = Class.class;//class java.lang.Class

3、类加载时机

  1. 加载:将class文件字节码内容鸡杂在到内存中,并将这些静态数据转换成方法去的运行时数据结构,然后生成一个代表这个类的java.lang.Class对象,并将其引用放入堆。
  2. 链接:将Java类的二进制代码合并到JVM的运行状态之中
    1. 验证:确保加载的类信息符合JVM规范,没有俺去那方面的问题
    2. 准备:为类的静态变量分配内存并将其初始化为默认值,都发生在方法区
    3. 解析:虚拟机看常量池内的符号引用(常量名)替换为直接引用
  3. 初始化:执行类构造器clinit()方法的过程。clinit()方法是由编译器自动收集类中的所有类变量的赋值动作和静态语句块(static{}块)中的语句合并产生的。
class SingleTon {
	private static SingleTon singleTon = new SingleTon();
	public static int count1;
	public static int count2 = 0;
	private SingleTon() {
		count1++;
		count2++;
	}
	public static SingleTon getInstance() {
		return singleTon;
	}
}
public class Test {
	public static void main(String[] args) {
		SingleTon singleTon = SingleTon.getInstance();
		System.out.println("count1=" + singleTon.count1); //1
		System.out.println("count2=" + singleTon.count2); //0
	}
}
/***
1.加载阶段将SingleTon的Class对象放置于方法区,并将其引用放于堆区
2.准备阶段为类变量SingleTon、count1、count2分配内存并赋予默认初始值null、0、0.
3.初始化阶段,依次初始化static变量的赋值以及static代码块
3.1赋值singleTon:调用初始化方法,count1.count2变为1
3.2count2赋值为0.
*/

Q1 何时开始类的加载?

虚拟机规范中并没强行约束,这点交给虚拟机的的具体实现自由把握。

Q2 何时开始类的初始化?

对一个类进行 “主动引用”时,如果类未初始化会对类进行初始化。除此种情况之外,均不会触发类的初始化,称为 “被动引用”。

主动引用

  1. new一个类的对象
  2. 访问类的静态变量和静态方法(除常量)
  3. 反射调用如(Class.forName())
  4. 当初始化一个类时,发现其父类还未初始化,则先出发父类的初始化
  5. 虚拟机启动时,定义了main()方法的那个类先初始化

被动引用

  1. 当访问一个静态域时,只有直接声明这个字段的类才会被初始化.如子类调用父类的静态变量,子类不会被初始化。只有父类被初始化。
  2. 通过数组定义来引用类,不会触发类的初始化
  3. 访问类的常量,不会初始化类(常量在链接阶段就存入调用类的常量池中了)

4、Class方法获取类的运行时结构

public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, NoSuchMethodException {
        Class c1 = Class.forName("User");
        //获取名字
        System.out.println(c1.getName());//获得包名+类名
        System.out.println(c1.getSimpleName());//获得类名
        //获取类的属性
        Field[] fields =c1.getFields(); //获取public属性
        fields =c1.getDeclaredFields();//所有属性
        for(Field f:fields){
            System.out.println(f);
        }
        Field f = c1.getField("gender"); //获取指定属性
        f=c1.getDeclaredField("name");
        System.out.println(f);
        //获取类的方法
        Method[] methods = c1.getMethods();
        methods = c1.getDeclaredMethods();
        for (Method method : methods) {
            System.out.println(method);
        }
        Method setName = c1.getMethod("setName", String.class);//获取指定方法,因为重载所以徐传入参数类型
        System.out.println(setName);
        //获取指定构造器
        Constructor[] constructors = c1.getConstructors();
        constructors = c1.getDeclaredConstructors();
        Constructor constructor = c1.getConstructor(null);
        constructor = c1.getConstructor(int.class,String.class,boolean.class);
        System.out.println(constructor);
    }

5、动态创建对象、执行方法

1、Class.newInstance()

  1. 类必须有一个无参数的构造器
  2. 类的构造器的访问权限需要足够
  3. 已弃用
Class c = User.class;
//Class.newInstance()构造对象
User user = (User)c.newInstance();
System.out.println(user);
//User{id=0, name='null', gender=false}

2、Class.getDeclaredConstructor(Class … parameterTypes)

Constructor constructor = c.getDeclaredConstructor(int.class, String.class);
user = (User) constructor.newInstance(10, "nb");
System.out.println(user);
//User{id=10, name='nb', gender=false}

3、反射调用普通方法

Method method = c.getMethod("setName", String.class);
method.invoke(user,"233");
System.out.println(user);
//User{id=10, name='233', gender=false}

public Object invoke(Object obj, Object... args)

  1. 如果原方法是静态的,则obj参数设为null。
  2. 原方法无返回值,则返回null
  3. 原方法形参列表为空,则args可为null
  4. 私有方法需使用getDeclaredMethod以及setAccessible方法

4、反射操作属性

操作私有属性时,需要关闭程序的权限检测,属性或者方法的setAccessible

accessible标志值为true表示反射对象在使用时应禁止检查 Java 语言访问控制。值为false表示反射对象在使用时应强制检查 Java 语言访问控制,

Field name = c.getDeclaredField("name");
name.setAccessible(true);
name.set(user,"666");
System.out.println(user);
//User{id=10, name='666', gender=false}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值