day43-反射02
2.Class类
2.1基本介绍
Class类也是类,因此也继承Object类
Class类对象不是new出来的,而是系统创建的
对于某个类的Class类对象,在内存中只有一份,因为类只加载一次
每个类的实例都会记得自己是由哪个Class实例所生成
通过Class对象可以得到一个类的完整结构(通过一系列API)
Class对象是存放在堆的
类的字节码二进制数据,是放在方法区的,有的地方称为类的元数据(包括 方法代码,变量名,方法名,访问权限等)
当我们加载完类之后,除了会在堆里生成一个Class类对象,还会在方法区生成一个类的字节码二进制数据(元数据)
例子:
package li.reflection.class_;
import li.reflection.Cat;
//对Class类的特点的梳理
public class Class01 {
public static void main(String[] args) throws ClassNotFoundException {
//1.Class类对象不是new出来的,而是系统创建的
//1.1.传统的 new对象
/**通过ClassLoader类中的loadClass方法:
* public Class<?> loadClass(String name) throws ClassNotFoundException {
* return loadClass(name, false);
* }
*/
//Cat cat = new Cat();
//1.2反射的方式
/**在这里debug,需要先将上面的Cat cat = new Cat();注释掉,因为同一个类只加载一次,否则看不到loadClass方法
* (这里也验证了:3.对于某个类的Class类对象,在内存中只有一份,因为类只加载一次)
* 仍然是通过 ClassLoader类的loadClass方法加载 Cat类的 Class对象
* public Class<?> loadClass(String name) throws ClassNotFoundException {
* return loadClass(name, false);
* }
*/
Class cls1 = Class.forName("li.reflection.Cat");
//2.对于某个类的Class类对象,在内存中只有一份,因为类只加载一次
Class cls2 = Class.forName("li.reflection.Cat");
//这里输出的hashCode是相同的,说明cls1和cls2是同一个Class类对象
System.out.println(cls1.hashCode());//1554874502
System.out.println(cls2.hashCode());//1554874502
}
}
Class类对象不是new出来的,而是系统创建的:
- 在
Cat cat = new Cat();
处打上断点,点击force step into,可以看到
- 注释
Cat cat = new Cat();
,在Class cls1 = Class.forName("li.reflection.Cat");
处打上断点,可以看到 仍然是通过 ClassLoader类加载 Cat类的 Class对象
2.2Class类常用方法
public static Class<?> forName(String className)//传入完整的“包.类”名称实例化Class对象
public Constructor[] getContructors() //得到一个类的全部的构造方法
public Field[] getDeclaredFields()//得到本类中单独定义的全部属性
public Field[] getFields()//得到本类继承而来的全部属性
public Method[] getMethods()//得到一个类的全部方法
public Method getMethod(String name,Class..parameterType)//返回一个Method对象,并设置一个方法中的所有参数类型
public Class[] getInterfaces() //得到一个类中锁实现的全部接口
public String getName() //得到一个类完整的“包.类”名称
public Package getPackage() //得到一个类的包
public Class getSuperclass() //得到一个类的父类
public Object newInstance() //根据Class定义的类实例化对象
public Class<?> getComponentType() //返回表示数组类型的Class
public boolean isArray() //判断此class是否是一个数组
应用实例
Car:
package li.reflection;
public class Car {
public String brand = "宝马";
public int price = 500000;
public String color ="白色";
@Override
public String toString() {
return "Car{" +
"brand='" + brand + '\'' +
", price=" + price +
", color='" + color + '\'' +
'}';
}
}
Class02:
package li.reflection.class_;
import li.reflection.Car;
import java.lang.reflect.Field;
//演示Class类的常用方法
public class Class02 {
public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchFieldException {
String classAllPath = "li.reflection.Car";
//1.获取到 Car类 对应的 Class对象
//<?>表示不确定的Java类型
Class<?> cls = Class.forName(classAllPath);
//2.输出cls
System.out.println(cls);//将会显示cls对象是哪个类的Class对象 class li.reflection.Car
System.out.println(cls.getClass());//输出cls的运行类型 class java.lang.Class
//3.得到包名
System.out.println(cls.getPackage().getName());//li.reflection :Class对象对应的类是在哪个包下面
//4.得到全类的名称
System.out.println(cls.getName());//li.reflection.Car
//5.通过cls创建一个对象实例
Car car = (Car)cls.newInstance();
System.out.println(car);//调用car.toString()
//6.通过反射获得属性 如:brand
Field brand = cls.getField("brand");
System.out.println(brand.get(car));//宝马
//7.通过反射给属性设置值
brand.set(car,"奔驰");
System.out.println(brand.get(car));//奔驰
//8.遍历得到所有的属性(字段)
Field[] fields = cls.getFields();
for (Field f:fields) {
System.out.println(f.getName());//依次输出各个属性字段的名称
}
}
}
3.获取Class类对象的各种方式
前提:已经知道一个类的全类名,且该类在类路径下,可通过Class类的静态方法forName()获取,可能抛出ClassNotFoundException
实例:
Class cls1 = Class.forName("java.lang.Cat");
应用场景:多用于配置文件,读取类全路径,加载类
前提:若已知具体的类,通过 类.class 获取,该方式最为安全可靠,程序性能最高
实例:
Class cls2 = Cat.class;
应用场景:多用于参数传递,比如通过反射得到对应构造器对象
前提:已某个类的实例,调用该实例的getClass()方法获取Class对象
实例:
Class cls3 = 对象.getClass();//运行类型
应用场景:通过创建好的对象,获取Class对象
其他方式
ClassLoader cl = 对象.getClass().getClassLoad();
Class cls4 = cl.loadClass("类的全类名");
基本数据类型
byte,short,int,long,double,float,boolean.char
, 按如下方式得到Class类对象Class cls = 基本数据类型.class
基本数据类型对应的包装类,可以通过
.TYPE
得到Class类对象Class cls = 包装类.TYPE
例子:
package li.reflection.class_;
import li.reflection.Car;
//演示得到Class对象的各种方式
public class getClass_ {
public static void main(String[] args) throws ClassNotFoundException {
//1.Class.forName
String classAllPath = "li.reflection.Car";//这里一般是通过配置文件获取全路径
Class cls1 = Class.forName(classAllPath);
System.out.println(cls1);//class li.reflection.Car
//2.类名.class ,多用于参数传递
Class cls2 = Car.class;
System.out.println(Car.class);//class li.reflection.Car
//3.对象.getClass() ,应用场景,有对象实例
Car car = new Car();
Class cls3 = car.getClass();
System.out.println(cls3);//class li.reflection.Car
//4.通过类加载器(4种)来获取到类的 Class对象
//(1)先得到car对象的类加载器(每个对象都有一个类加载器)
ClassLoader classLoader = car.getClass().getClassLoader();
//(2)通过类加载器得到Class对象
Class cls4 = classLoader.loadClass(classAllPath);
System.out.println(cls4);//class li.reflection.Car
//cls1,cls2,cls3,cls4其实是同一个Class对象
System.out.println(cls1.hashCode());//1554874502
System.out.println(cls2.hashCode());//1554874502
System.out.println(cls3.hashCode());//1554874502
System.out.println(cls4.hashCode());//1554874502
//5.基本数据类型按如下方式得到Class类对象
Class<Integer> integerClass = int.class;
Class<Character> characterClass = char.class;
Class<Boolean> booleanClass = boolean.class;
System.out.println(integerClass);//int
System.out.println(characterClass);//char
System.out.println(booleanClass);//boolean
//6.基本数据类型对应的8种包装类,可以通过 .TYPE得到Class类对象
Class<Integer> type1 = Integer.TYPE;
Class<Character> type2 = Character.TYPE;
System.out.println(type1);
System.out.println(integerClass.hashCode());//1846274136
System.out.println(type1.hashCode());//1846274136
}
}
4.哪些类有Class对象
- 外部类,成员内部类,静态内部类,局部内部类,匿名内部类
- interface:接口
- 数组
- enum:枚举
- annotation:注解
- 基本数据类型
- void
例子:
package li.reflection.class_;
import java.io.Serializable;
//演示哪些类有Class对象
public class allTypeClass {
public static void main(String[] args) {
Class<String> cls1 = String.class;//外部类
Class<Serializable> cls2 = Serializable.class;//接口
Class<Integer[]> cls3 = Integer[].class;//数组
Class<float[][]> cls4 = float[][].class;//二维数组
Class<Deprecated> cls5 = Deprecated.class;//注解
//Thread类中的枚举State--用来表示线程状态
Class<Thread.State> cls6 = Thread.State.class;//枚举
Class<Long> cls7 = long.class;//基本数据类型
Class<Void> cls8 = void.class;//void类型
Class<Class> cls9 = Class.class;//Class类也有
System.out.println(cls1);//class java.lang.String
System.out.println(cls2);//interface java.io.Serializable
System.out.println(cls3);//class [Ljava.lang.Integer;
System.out.println(cls4);//class [[F
System.out.println(cls5);//interface java.lang.Deprecated
System.out.println(cls6);//class java.lang.Thread$State
System.out.println(cls7);//long
System.out.println(cls8);//void
System.out.println(cls9);//class java.lang.Class
}
}
5.类加载
基本说明:
反射机制是java实现动态语言的关键,也就是通过反射实现类动态加载
静态加载:编译时加载相关的类,如果没有则报错,依赖性太强
静态加载的类,即使没有用到也会加载,并且进行语法的校验
动态加载:运行时加载相关的类,如果运行时不用该类,即使不存在该类,也不会报错,降低了依赖性
类加载的时机:
- 当创建对象时(new)//静态加载
- 当子类被加载时 //静态加载
- 调用类中的静态成员时 //静态加载
- 通过反射 //动态加载
例子:静态加载和动态加载
import java.lang.reflect.*;
import java.util.*;
public class classLoad_ {
public static void main(String[] args) throws Exception {
Scanner scanner = new Scanner(System.in);
System.out.println("请输入key");
String key = scanner.next();
switch (key) {
case "1":
Dog dog = new Dog();//静态加载,依赖性很强
dog.cry();
break;
case "2":
//反射 -->动态加载
Class cls = Class.forName("Person"); //加载Person[动态加载]
Object o = cls.newInstance();
Method m = cls.getMethod("hi");
m.invoke(o);
System.out.println("ok");
break;
default:
System.out.println("do nothing...");
}
}
}
//因为new Dog()是静态加载,因此必须编写Dog
//Person类是动态加载,所以即使没有编写Person类也不会报错,只有当动态加载该类时,(有问题)才会报错
class Dog{
public void cry(){
System.out.println("小狗在哭泣..");
}
}
在没有编写Dog类时,即使在switch选择中,不一定会运行到new dog对象的case1,但是程序仍然报错了,因为静态加载的类,即使没有用到,也会加载,并且进行语法的校验
在编写了Dog类对象后,可以看到编译通过:
运行程序:可以看到,即使没有编写Person类,但是运行时没有用到,就不会报错
使用到Person类,报错:(运行时加载)
6.类的加载过程
- 类加载过程图
- 类加载各阶段完成的任务
- 加载阶段:将类的class文件读入内存,并为之创建一个java.lang.Class对象。此过程由类加载器完成。
- 连接阶段:将类的二进制数据合并到jre中
- 初始化阶段:JVM负责对类进行初始化,这里主要是指静态成员
6.1.1加载阶段
JVM 在该阶段的主要目的是,将字节码从不同的数据源(可能是class文件,也可能是jar包,甚至网络)转化为二进制字节流加载到内存中,并声称一个代表该类的java.lang.Class对象
6.1.2连接阶段-验证
- 目的是为了确保Class文件的字节流中包含的信息符合当前虚拟机的要求,并且不会危害虚拟机自身的安全
- 包括:文件格式验证(是否以 魔数 oxcafebabe开头)、元数据验证、字节码验证和符号引用验证
- 可以考虑使用 -Xverify:none 参数关闭大部分的类验证措施,缩短虚拟机类加载的时间
6.1.3连接阶段-准备
JVM会在该阶段对静态变量,分配内存并默认初始化(对应的数据类型的默认初始值,如0,0L,null,false等)。这些变量所使用的内存都将在方法区中进行分配
例如:
package li.reflection.classload_;
//我们说明一个类加载的链接阶段-准备
public class ClassLoad02 {
public static void main(String[] args) {
}
}
class A {
//属性-成员变量-字段
//一个类加载的链接阶段中的准备阶段 属性是如何处理的
//1. n1 是实例变量,不是静态变量,因此在准备阶段,是不会分配内存的
//2. n2 是静态变量,分配内存 n2,且默认初始化为 0,而不是20
//3. n3 是static final,是常量,它和静态变量不一样,因为一旦赋值就不变,n3 = 30
public int n1 = 10;
public static int n2 = 20;
public static final int n3 = 30;
}
6.1.4连接阶段-解析
虚拟机将常量池内的符号引用替换为直接应用的过程
6.1.5初始化阶段
- 到初始化阶段,才真正开始执行类中定义的Java程序代码,此阶段是执行
<clinit>()
方法的过程 <clinit>()
方法是 由编译器按语句在源文件中出现的顺序,依次自动收集类中的 所有静态变量 的赋值动作 和 静态代码块中的语句,并进行合并。-->例子1- 虚拟机会保证一个类的
<clinit>()
方法在多线程环境中被正确地加锁、同步,如果多线程同时去初始化一个类,那么只会有一个线程去执行这个类的<clinit>()
方法,其他线程都需要阻塞等待,直到活动线程执行<clinit>()
方法完毕。
例子1:演示类加载的初始化阶段
package li.reflection.classload_;
//演示类加载的初始化阶段
public class ClassLoad03 {
public static void main(String[] args) {
//分析:
/**
* 1.加载B类,并生成 B的Class对象
* 2.链接 :将num默认初始化为 0
* 3.初始化阶段:
* 3.1依次 自动收集类中的 所有静态变量的赋值动作 和 静态代码块中的语句,并合并
* 收集:
* clinit(){
* System.out.println("B的静态代码块被执行");
* num = 300;
* num = 100;
* }
* 合并:num =100;
*/
//直接使用类的静态属性也会导致类的加载
System.out.println(B.num);//100
}
}
class B {
static {
System.out.println("B的静态代码块被执行");
num = 300;
}
static int num = 100;
public B() {
System.out.println("B的构造器被执行");
}
}
例子2:
在例子1中的程序里创建一个B类对象,打上断点,debug源码:
可以看到在底层中,使用了对象锁synchronized (getClassLoadingLock(name))
:
也就是说,加载类的时候,是有类的同步控制机制。
正因为有这个机制,才能保证某个类在内存中,只有一份Class对象。
day43-反射02的更多相关文章
- Java反射02 : Class对象获取的三种方式和通过反射实例化对象的两种方式
1.Class对象获取的三种方式 本文转载自:https://blog.csdn.net/hanchao5272/article/details/79361463 上一章节已经说过,一般情况下,Jav ...
- C#反射 入门学习 02
获取Type类型的构造函数 前言 有了前面的 C#反射 入门学习 01的知识,学习这篇估计是搓搓有余,它教会了我们获取方法的相关信息的两种形式与 使用反射调用方法, ...
- java反射详解 (转至 http://www.cnblogs.com/rollenholt/archive/2011/09/02/2163758.html)
本篇文章依旧采用小例子来说明,因为我始终觉的,案例驱动是最好的,要不然只看理论的话,看了也不懂,不过建议大家在看完文章之后,在回过头去看看理论,会有更好的理解. 下面开始正文. [案例1]通过一个对象 ...
- 「JavaSE 重新出发」05.03.02 在运行时使用反射分析对象
在编写程序时,如果知道想要查看的域名和类型,查看指定的域是一个很容易的事,而利用反射机制可以查看在编译时还不清楚的对象域. java Employee tank = new Employee(&quo ...
- 02.反射Reflection
1. 基本了解 1.1 反射概述 文字说明 审查元数据并收集关于它的类型信息的能力称为反射,其中元数据(编译以后的最基本数据单元)就是一大堆的表,当编译程序集或者模块时,编译器会创建一个类定义表,一个 ...
- java反射机制入门02
Field对象的机制与实现 1.Field对象概述 1)Java.lang.reflect.Field类,是用于表示类中.接口中属性对象的类. 2)可以操作类中私有,以及公有等全部属性和属性的信息. ...
- .NET面试题系列[6] - 反射
反射 - 定义,实例与优化 在面试中,通常会考察反射的定义(操作元数据),可以用反射做什么(获得程序集及其各个部件),反射有什么使用场景(ORM,序列化,反序列化,值类型比较等).如果答得好,还可能会 ...
- 详解C#中的反射
反射(Reflection) 2008年01月02日 星期三 11:21 两个现实中的例子:1.B超:大家体检的时候大概都做过B超吧,B超可以透过肚皮探测到你内脏的生理情况.这是如何做到的呢?B超是B ...
- Python模块之常用模块,反射以及正则表达式
常用模块 1. OS模块 用于提供系统级别的操作,系统目录,文件,路径,环境变量等 os.getcwd() 获取当前工作目录,即当前python脚本工作的目录路径 os.chdir("di ...
- Java中的反射和注解
前言 在Java中,反射机制和注解机制一直是一个很重要的概念,那么他们其中的原理是怎么样呢,我们不仅仅需要会使用,更要知其然而之所以然. 目录 反射机制 反射如何使用 注解定义 注解机制原理 注解如何 ...
随机推荐
- 01-vscode自定义配色方案 插件基础上
01-下载相关主题插件 02- 点击设置按钮 复制id 03-进入插件文件 C:\Users\Administrator\.vscode\extensions 04-复制刚才的id 05-themes ...
- 【水】关于 __attribute__
目录 1. weak 2. aligned 3. __packed__ 4. always_inline 1. weak 实现如果 Func1 被定义了,我就调用 Func1, 否则就调用 MyFun ...
- mysql防SQL注入搜集
SQL注入 例:脚本逻辑 $sql = "SELECT * FROM user WHERE userid = $_GET[userid] "; 案例1:SELECT * FROM ...
- ASP.NET Core依赖注入系统学习教程:关于服务注册使用到的方法
在.NET Core的依赖注入框架中,服务注册的信息将会被封装成ServiceDescriptor对象,而这些对象都会存储在IServiceCollection接口类型表示的集合中,另外,IServi ...
- Gulp介绍及安装使用教程
一.简介 gulp是前端开发过程中对代码进行构建的工具,是自动化项目的构建利器,不仅能对网站资源进行优化,而且在开发过程中很多重复的任务能够使用正确的工具自动完成,使用她,我们不仅可以很愉快的编写代码 ...
- Redis 18 Jedis
参考源 https://www.bilibili.com/video/BV1S54y1R7SB?spm_id_from=333.999.0.0 版本 本文章基于 Redis 6.2.6 概述 Jedi ...
- 服务器部署 Vue 和 Django 项目的全记录
本篇记录我在一个全新服务器上部署 Vue 和 Django 前后端项目的全过程,内容包括服务器初始配置.安装 Django 虚拟环境.python web 服务器 uWSGI 和反向代理 Nginx ...
- 洛谷P4135 作诗(不一样的分块)
题面 给定一个长度为 n n n 的整数序列 A A A ,序列中每个数在 [ 1 , c ] [1,c] [1,c] 范围内.有 m m m 次询问,每次询问查询一个区间 [ l , r ] [l, ...
- 无密码正向直连内网linux目标机复现
无密码正向直连内网linux目标机复现 文章来自sxf大佬klion https://mp.weixin.qq.com/s/GPPvci8qKuvc5d3Q7Cer7Q 场景说明 前期通过一些 Rce ...
- vue2和vue3的区别?
vue2和vue3的主要区别在于以下几点: 1.生命周期函数钩子不同 2.数据双向绑定原理不同 3.定义变量和方法不同 4.指令和插槽的使用不同 5.API类型不同 6.是否支持碎片 7.父子组件之间 ...