目录
三、三种方式获取Class对象【一个class的Class实例】
一、引入Class类
二、定义
对于任意一个类,在运行期间加载类,并允许以编程的方式解剖类中的各个成分 并映射成相应的Java类【位于Java.lang.reflect包中】
各个成分 | 相对应的类 |
包 | Package |
构造方法 | Constructor |
属性 | Field |
方法 | Method |
发生在运行期
【即:将编译好后的字节文件交给计算机执行,把磁盘中的代码放到内存中存起来】
目的
为了获取某个实例的信息
三、三种方式获取Class对象【一个class的Class实例】
方式一:
Class c1 = 类名.class
JVM使用类装载器,将类装入内存(前提是:类还没有装入内存),只加载一次
不做类的初始化工作,返回Class的对象。
方式二:
使用类名调用Class提供静态方法:【Public static Class forName(String package)】
即:Class.forName(“类名字符串”)
装入类,并做类的静态初始化,返回Class的对象
方式三:
Object提供的方法:pubic Class getClass()】
即:实例对象.getClass()
返回引用运行时真正所指的对象所属类的Clas的对象
真正:(子对象的引用会赋给父对象的引用变量中)
代码整理:
public static void main(String[] args) throws ClassNotFoundException {
//方式1:通过类名 访问class
Class stringCls1 = String.class;
//方式2:通过实例访问class
String s = "";
Class stringCls2 = s.getClass();
//方式3:通过Class类的静态方法forName(类名)访问class
Class stringCls3 = Class.forName("java.lang.String");
//得到相同的Class对象
System.out.println(stringCls1.hashCode());
System.out.println(stringCls2.hashCode());
System.out.println(stringCls3.hashCode());
}
结果:
结果分析
因为Class实例在JVM中是唯一的,所以,上面方法获取的Class实例为同一个实例
可以用==来比较两个Class实例是否为同一个
四、通过反射可以获取某个实例的信息
4.1、获取Class类的类信息
- getName() 返回完全限定名【包名+类名】
- getSimpleName() 返回类名
- getTypeName() 返回类型信息
public class classInfo {
public static void main(String[] args) {
Class clazz = List.class;
printClassInfo(clazz);
}
public static void printClassInfo(Class clazz) {
System.out.println("名称:"+clazz.getSimpleName());
System.out.println("完全限定名:"+clazz.getName());
System.out.println("类型名称:"+clazz.getTypeName());
System.out.println();
}
}
4.2、获取Class类的包信息
Package pck = clazz.getPackage();
public class Test {
public static void main(String[] args) {
Class clazz = String.class;
printClassInfo(clazz);
}
public static void printClassInfo(Class clazz) {
Package pck = clazz.getPackage();
if (pck != null) {
System.out.println("该类所在的包的名称:" + pck.getName());
}
}
}
4.3.获取接口信息
System.out.println("实现接口列表");
try {
Class[] inf = String.class.getInterfaces();
for(Class f :inf) {
System.out.println(f);
}
} catch (Exception e) {
e.printStackTrace();
}
4.5、获取继承信息
获取类,并调用getSuperclass()方法
Class clazz = FileInputStream.class.getSuperclass();
System.out.println("父类:" + clazz.getName());
4.5、判断类型
System.out.println("是否为接口:"+clazz.isInterface());
System.out.println("是否是数组:"+clazz.isArray());
System.out.println("是否为枚举:"+clazz.isEnum());
System.out.println("是否为基本类型:"+clazz.isPrimitive());
五、创建实例对象
newInstance()
public static void main(String[] args) throws IOException {
//从文件中读取类的名称
String className = Files.readAllLines(Paths.get("F:\\test\\23.txt")).get(0);
try {
//【得到】该类的Class对象
Class clazz = Class.forName(className);
//创建3个对象
Object obj1 = clazz.newInstance();
Object obj2 = clazz.newInstance();
System.out.println(obj1);
System.out.println(obj2);
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
六、Constructor类 访问构造方法
Constructor类封装了构造方法中的所有信息,可以通过Constructor实例来获取
相关方法
应用
基础类---对该类进行相关方法调用
class Example{
public Example() {
System.out.println("Example类的无参构造");
}
private Example(String s) {
System.out.printf("Example类的私有构造:一个参数:s ,s=%d\n",s);
}
public Example(int a) {
System.out.printf("Example类的有参构造:一个参数:a ,a=%d\n",a);
}
public Example(int a,double b) {
System.out.printf("Example类的有参构造:一个参数:a ,a=%d,b=%f\n",a,b);
}
}
应用1:
public class Demo{
public static void main(String[] args) {
Class clazz = Example.class;
try {
//调用无参构造方法创建Example对象
Example ex1 = (Example)clazz.newInstance();
System.out.println(ex1);
System.out.println();
//获取所有的构造方法
System.out.println("获取所有的构造方法");
Constructor[] constructors = clazz.getConstructors();
for(Constructor constructor :constructors) {
System.out.println(constructor);
}
System.out.println();
System.out.println("使用构造器调用不同参数类型的构造方法来创建Example对象");
//调用有参构造方法创建Example对象
Constructor construct = clazz.getConstructor();
System.out.println("无参构造:"+construct);
Constructor construct2 = clazz.getConstructor(int.class);
System.out.println(construct2);
Constructor construct3 = clazz.getConstructor(int.class,double.class);
System.out.println(construct3);
}catch (Exception e) {
//异常处理省略
}
}
}
应用2:获取私有构造方法并调用
注意:通过设置setAccessible(true)才能访问非public修饰的构造方法
从这里可以看出:反射破坏了封装性【硬编码是不能调用私有构造】
public static void main(String[] args) {
Class clazz = Example.class;
Constructor privateConstruct;
try {
privateConstruct = clazz.getDeclaredConstructor(String.class);
//调用
privateConstruct.setAccessible(true);
Example ex = (Example)privateConstruct.newInstance("private-construct");
} catch (Exception e) {
//异常处理省略
}
}
instaceof运算符
解释:判断引用和类型是否匹配
作用:用来判断继承关系
public static void main(String[] args) {
String s = "username";
System.out.println("是否为String类型:"+(s instanceof String));
System.out.println("");
}
isAssignableForm()方法
解释:判断类型与类型之间是否可以赋值
作用:
1.子类可以赋值给父类引用
2.接口可以赋值给实现类
//isAssignableFrom()方法:判断“类型”和类型”之间关系
System.out.println("Integer类型是否可以赋值给Integer类型?"+Integer.class.isAssignableFrom(Integer.class));
System.out.println("Number类型是否可以赋值给Integer类型?"+Integer.class.isAssignableFrom(Number.class));
System.out.println("Integer类型是否可以赋值给Number类型?"+Number.class.isAssignableFrom(Integer.class));
System.out.println("Integer类型是否可以赋值给Double类型?"+Double.class.isAssignableFrom(Integer.class));
System.out.println("Integer类型是否可以赋值给Comparable类型?"+Comparable.class.isAssignableFrom(Integer.class));
七、Field类 访问字段【成员变量】
在反射中 ,每个字段都会被封装成一个Field对象。
对任意一个Object实例,只要获取了它的class,就可以获取它的一切信息。
应用
Eg1:访问Book类的字段
class Book{
public String Bookname;
private int stock;
private double sale;
//getter and setter 省略
@Override
public String toString() {
return "Book [Bookname=" + Bookname + ", stock=" + stock + ", sale=" + sale + "]";
}
}
注意:"访问修符符:" : Modifier.toString(field.getModifiers())
field.getModifiers() 得到的是int类型的值
public class Demo09_Field {
public static void main(String[] args) {
Class clazz = Book.class;
Field[] publicfiels = clazz.getFields();
Field[] fields = clazz.getDeclaredFields();
for(Field field :fields) {
System.out.println("成员变量访问修饰符【int】:"+field.getModifiers());
System.out.println("成员变量访问修符:"
+Modifier.toString(field.getModifiers()));
System.out.println("成员变量类型:"+field.getType());
System.out.println("成员变量名称:"+field.getName());
System.out.println();
}
}
}
Eg2:访问[获取]Book类的字段的值
在反射中能访问字段,就是为了能够给该字段赋值
使用方法:Field.get(Object obj)
对于非public修饰的字段,要设置:setAccessible(true)
public class Demo11_getFieldValue {
public static void main(String[] args) throws IllegalArgumentException, IllegalAccessException {
Book book = new Book();
book.setBookname("一笙有喜");
book.setStock(500);
book.setSale(0.75);
printInfo(book);
}
public static void printInfo(Object obj) throws IllegalArgumentException, IllegalAccessException {
Class clazz = obj.getClass();
Field[] fields = clazz.getDeclaredFields();
for(Field field : fields) {
System.out.println("成员变量名称:"+field.getName());
if(!field.isAccessible()) {
field.setAccessible(true);
}
System.out.println("成员变量内容:"+field.get(obj));
System.out.println();
}
}
}
Eg3:设置Book类的字段的值
Field.set(Object【指定的实例对象】, Object【待修改的值】)
public static void main(String[] args) {
Class clazz = Book.class;//1.获取Class类的对象
try {
Object obj = clazz.newInstance();
Field field = clazz.getDeclaredField("name");
field.set(obj, "红楼梦");
System.out.println(obj);
} catch (Exception e) {
//……
}
}
八、Method类 访问方法
与Constructor类和Field类似,基本方法如下:
一个Method对象 包含一个方法的所有信息:
getModifiers() 返回方法的修饰符 [intl类型]
getName() 返回方法名称
getReturnType() 返回方法返回值类型【一个Class实例】
getParameterTypes() 返回方法的参数类型【一个Class数组】
public static void main(String[] args) {
Class clazz = Book.class;
Method[] methods = clazz.getMethods();
for(Method m: methods) {
System.out.println("方法的名称:"+m.getName());
System.out.println("方法的访问修饰符:"+Modifier.toString(m.getModifiers()));
System.out.println("方法的返回值类型:"+m.getReturnType());
Parameter[] params =m.getParameters();
for(Parameter p :params) {
System.out.println("参数名称:"+p.getName());
System.out.println("参数类型:"+p.getType());
System.out.println();
}
System.out.println();
}
}
Eg1:调用方法
调用某个对象的方法:Object invoke(Object instance,Object..·parameters)
public static void main(String[] args) throws Exception{
Class clazz = Base.class;
Object obj = clazz.newInstance();
//按照方法名称和"参数类型"获取Method方法对象
//无参方法
// Method method = clazz.getMethod("create");
// int result = (int)method.invoke(obj);
//有参方法
Method method = clazz.getMethod("create",int.class);
int result = (int)method.invoke(obj, 100);
System.out.println(result);
}
Eg2:调用静态方法
获取某个指定数字的位数
public static void main(String[] args) throws Exception {
//方式1:
System.out.println((int)Math.log10(1234567765)+1);
//方式2:反射调用
Class clazz = Math.class;
Method method = clazz.getMethod("log10", double.class);
int size = Double.valueOf((double)method.invoke(null, 1234567765)).intValue()+1;
System.out.println(size);
}
Eg3:多态问题【目标对象决定调用方法的归处】
public class Demo {
public static void main(String[] args) throws Exception{
//获取Person的hello方法:
Method h = Person.class.getMethod("hello");
//对Student实例调用hello方法:
h.invoke(new Student());
}
}
class Person {
public void hello() {
System.out.println("Person:hello");
}
}
class Student extends Perrson {
public void hello() {
System.out.println("Student:hello");
}
}
结果:
小结:通过反射调用方法时,仍然遵循多态原则