获取类对象的方式
方式1:
在编写静态源代码阶段,使用Class.forName+类全路径
应用场景:读取配置文件 ——> 读取类的全路径 ——> 加载类,
这种方式常用在底层框架中
方式2:
在Class类阶段,直接使用 类.class
应用场景:用于把类作为参数传递
方式3:
在代码运行阶段时,
当创建了事例对象时,通过对象名.getClass方法
方式4:
使用类加载器【类加载器一共有4种】
classLoader.loadeClass()
基本数据类型和其包装类的类
基本数据类型都是装箱成其包装类,在需要成为基本数据类型的时候再拆箱为基本数据类型
源代码事例:
public static void main(String[] args) throws ClassNotFoundException {
//1、Class.forName
//一般是在读取配置文件获取全路径
String classAllPath = "classLoader.User";
Class<?> userClass1 = Class.forName(classAllPath);
System.out.println(userClass1);
//获取类名:out:classLoader.User
System.out.println(userClass1.getName());
//获取类类型:out:class java.lang.Class
System.out.println(userClass1.getClass());
//2、类名.class,用于参数传递
Class<User> userClass2 = User.class;
System.out.println(userClass2);
//3、对象.getClass,在已有对象事例
User user = new User();
Class<? extends User> userClass3 = user.getClass();
System.out.println(userClass3);
//4、通过类加载器获取类的Class对象,类加载器一共有四种
ClassLoader classLoader = ClassLoader.getSystemClassLoader();
Class<?> userClass4 = classLoader.loadClass("classLoader.User");
System.out.println(userClass4);
//因为同个类,其实只会存储一个类对象在堆中,这样其实上面我们加载了那么多次,其实都是同一个Class对象。
System.out.println(userClass1.hashCode());
System.out.println(userClass2.hashCode());
System.out.println(userClass3.hashCode());
System.out.println(userClass4.hashCode());
//5、基本数据类型获取Class对象
Class<Integer> integerClass = int.class;
Class<Character> characterClass = char.class;
Class<Boolean> booleanClass = boolean.class;
System.out.println(integerClass);
//6、利用基本数据类型的包装类.TYPE
Class<Integer> integerClass1 = Integer.class;
System.out.println(integerClass1);
Class<Integer> type = Integer.TYPE;
System.out.println(type);
//int 是 通过装箱成Integer再 拆箱出来。
}
具有Class对象的类型
具有Class对象的类型有:
- 外部类
- 内部类
- 成员内部类
- 静态内部类
- 局部内部类
- 匿名内部类
- 数组
- 枚举类 enum
- 注解
- 基本数据类型
- void
//1.外部类
Class<User> userClass = User.class;
//接口
Class<Serializable> serializableClass = Serializable.class;
//2.数组
Class<String[]> aClass = String[].class;
//二维数组
Class<int[][]> iaClass = int[][].class;
//3.注解
Class<Deprecated> deprecatedClass = Deprecated.class;
//4.枚举
Class<Thread.State> stateClass = Thread.State.class;
//5.基本数据类型
Class<Integer> integerClass = int.class;
Class<Integer> integerClass1 = Integer.class;
//6.void
Class<Void> voidClass = void.class;
类加载
之所以java时动态语言,反射机制时关键,而反射机制的实现就是通过动态加载。
静态加载和动态加载
-
静态加载:编译时加载相关类,如果没有则报错,依赖性极强
new User();
-
动态加载:编译不会报错,运行时加载相关类,如果在运行时没有使用相关类则不会报错,降低了依赖性。
Class.forName("User");
在类加载的3个阶段中,加载和连接都是JVM实现的。
加载过程是将字节码(class文件、jar包、网络等)转换为二进制字节流加载到内存中,并生成一个Class对象。
连接过程中的验证是验证字节流的合法性如:是否以魔数0xcafebabe开头等。
连接过程中的准备是jvm对静态变量分配内存并默认初始化值(比如说int默认初始化就是0),分配在方法区。
连接过程中的解析阶段时jvm将常量池的符号引用转为直接应用的过程,这个概念在汇编可以想象得到,符号引用只是知道了这个标签,但是具体的引用需要去知道对应内存地址才可以进行真正的调用。
初始化过程是clint()方法由编译器按照语句在源文件的出现的顺序,依次自动收集所有静态变量的赋值操作和静态代码块的语句,并进行合并。
反射练习题
通过反射修改私有变量,并调用方法获取修改后的值
public static void main(String[] args)
throws ClassNotFoundException, IllegalAccessException, Instantiati
onException, NoSuchFieldException, NoSuchMethodException, Invocati
onTargetException {
Class<?> aClass = Class.forName("classLoad.User");
Method getName = aClass.getMethod("getName");
Object o = aClass.newInstance();
//获取非公有成员对象需要用DeclaredXXX
Field name = aClass.getDeclaredField("name");
System.out.println(getName.invoke(o));
//想要操作非公用的就需要把这个设置为true,构造、方法都一样
name.setAccessible(true);
name.set(o,"sutpc");
System.out.println(getName.invoke(o));
}
}