Java中的反射机制详解
Java中的反射机制详解
1. Java中的反射
1.什么是反射?
2.如何用反射的机制?Java的框架如何使用反射的?
3.Java反射十分重要,反射的知识点对于Java底层,Java框架的学习都有帮助。
2. Class类
1.什么是Class类?
2. 万事万物皆对象。
3.但是在java中,基础类型与Java静态(static)修饰的东西并非是对象。
4.类(这里指的是平常编程里,我们定义的类)是不是对象呢?如果是一个对象,那么类是谁的对象(实例)呢?
- 类是java.lang.Class的实例对象
- There is a class named Class.
即:任何一个类都是Class类的实例对象,Class类的实例对象我们称之为 类(类型) 。且这个对象有三种实例化方式: - a:
类名.class
- b:某类对象的
getClass()
方法 - c:
Class.forName("类名");
针对上述规则,有代码如下:
package shen.liu.enmonster;
public class Test {
public Test(){
System.out.println("类Test的实例化对象成功");
}
public static void main(String []args) throws ClassNotFoundException {
//1.通过第一种方式获得类类型
//我们直接可以通过 类名.class 的方式来获取一个类类型 --> 说明任何一个类都有一个隐藏的静态成员变量class
Class c1 = Test.class;
System.out.println(c1);
//2.通过第二种方法获得类类型
//新建一个普通类(本例中是Test类)的实例对象,再通过Test的实例对象来获取Test的类类型
Test t = new Test();
Class c2 = t.getClass();
System.out.println(c2);
//3.通过第三种表达方式获得类类型
Class c3 = null;
c3 = Class.forName("shen.liu.enmonster.Test");
System.out.println(c3);
try {
Test t2 = (Test)c1.newInstance();//返回成Test对象
} catch (Exception e) {
e.printStackTrace();
}
}
}
注意判别两种实例对象:
- 一个是Class类的实例对象,这个实例对象就是普通的类。
- 另外一个就是普通类的实例对象,也就是我们通常new出来的实例对象。
我们完全可以通过类的类类型来创建类的对象,如:c1.newInstance();
3. Class的动态加载类
1.什么是动态加载类?
编译时刻加载类:静态加载类;
运行时刻加载类:动态加载类。
需要区别编译,运行。
2.Class.forName("ClassName");
4.new 创建对象是静态加载类,不管有没有用到该对象,在编译时刻都会加载。
5.用到哪个就加载哪个,所以根据这个我们可以使用动态加载类。
7.对于功能性的类,我们最好使用动态加载类,而非是静态加载类。
比如说,如果我们需要用到一个类中的某个方法,但是该类中的其它方法存在问题,如果采用静态加载,则会导致该类无法使用。此时我们就应该使用动态加载(即在运行时来判断是否加载该无法运行的方法,如果不是加载“无法运行的方法”,则该类可以正常使用)。
4. 实例
4.1 验证动态/静态加载
package shen.liu.enmonster;
public class Office {
public static void main(String []args){
if(args[0].equals("Excel")){//如果该参数是Excel 则加载Excel类的实例
Excel e = new Excel();
}
else if(args[0].equals("Word")){
Word w = new Word();
}
}
}
如果我们仅仅将这个类放到shell窗口运行的话,则会报错。
//package shen.liu.enmonster;
//在使用DOS环境下编译.java文件时,最好不使用包
public class Office {
public static void main(String []args){
if(args[0].equals("Word")){
Word w = new Word();
w.function();
}
else if(args[0].equals("Excel")){
Excel e = new Excel();
e.function();
}
}
}
运行结果如下:
报错的原因是:无法找到Word/Excel两个类。但是我们可能仅仅只是编译一下,却不使用这两个类。当使用命令:javac Office.java
时,仍然会把该类的所有对象都给实例化一遍。 导致在寻找Word和Excel时出错!这就是静态加载带来的毛病!
这个并非我们想要的结果,因为我们传递的参数可能既不是Word,也不是Excel。我们该怎么解决这个问题呢?我们使用动态加载即可解决问题,代码如下:
public class OfficeBetter {
public static void main(String []args)
throws IllegalAccessException, InstantiationException {
Class c = null;
try {
c = Class.forName(args[0]);//根据Class反射机制 和传进来的参数 来创建一个类类型
//我们无法知道这个参数args[0]到底是什么,也就是说我们无法知道这个类型是Word还是Excel
//所以我们使用一个接口,来让Word和Excel来继承,然后返回的实例类型是接口名即可
OfficeAble oa = (OfficeAble) c.newInstance();
oa.function();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
修改完成之后,编译Word.java文件,然后使用命令java OfficeBetter Word
运行成功!
2.使用Class的API进行操作
- 利用反射获取成员方法
package shen.liu.enmonster;
import java.lang.reflect.Method;
public class Test {
public Test(){
System.out.println("类Test的实例化对象成功");
}
public static void main(String []args) {
Class c1 = int.class;//int 的类类型
Class c2 = String.class;//String 的类类型
Class c3 = double.class;
Class c4 = Double.class;
Class c5 = void.class;
//基本的数据类型,void关键字都存在类类型
// System.out.println(c3);//作用同 System.out.println(c3.getName());
// System.out.println(c4);
// System.out.println(c4.getSimpleName());
//Class的基本API操作
//获取任意类的全部信息
//打印类的信息,包括类的成员函数,成员变量
String str = "hello";
printClassMessage(str);
}
public static void printClassMessage(Object obj) {
//要获取类的信息,首先要获取类的类类型
Class c = obj.getClass();//传递的是哪个子类的对象 c就是该子类的类类型
//获取类的名称
System.out.println("类全名为:"+c.getName());
//万事万物皆对象,方法也是对象。方法是Method的对象
/**
* 1.一个成员方法,就是一个Method对象
* 2.getMethod()方法获取的是所有的public的函数,包括父类继承而来
* 3.getDeclaredMethods()获取的是所有该类自己声明的方法,无论访问权限是什么
*/
Method[] ms = c.getMethods();//获取该类的所有方法
System.out.println("返回类型 "+"方法名 " +"参数类型 ");
for(Method m :ms){//一个方法循环一次
//得到方法的返回值类型
Class returnType = m.getReturnType();
System.out.print(returnType.getName()+" ");
//得到方法的名称
System.out.print(m.getName()+"(");
//获取参数类型 --->得到的是参数列表的类型的类类型
Class[] paramTypes = m.getParameterTypes();
for(Class cl : paramTypes){
System.out.print(cl.getName()+" ");
}
System.out.println(")");
}
}
}
- 利用反射获取成员变量
package shen.liu.enmonster;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
/**
* 1.获取成员变量的信息
* 2.成员变量也是对象 java.lang.reflect.Field
* Field类封装了关于成员变量的操作
* getFields()方法获取的是所有public的成员变量的信息
* getDeclaredFields获取的是该类自己声明的成员变量的信息
*/
public class Test {
public Test(){
System.out.println("类Test的实例化对象成功");
}
public static void main(String []args) {
String str = "love";
try {
test(str);
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
}
public static void test(Object obj) throws NoSuchMethodException {
Class c1 = obj.getClass();
Field fs[] = c1.getDeclaredFields();
for(Field f : fs){
//得到成员变量的类型的类类型
Class c2 = f.getType();
String fName = c2.getName();
System.out.println("类型:"+fName +" 成员名称:"+f.getName());
}
/**
* 构造函数也是对象
* java.lang.Constructor中封装了构造函数的信息
* getConstructors获取了所有的public的构造函数
* getDeclaredConstructors得到所有的构造函数
*
*/
}
}
- 利用反射,获取某个方法
package shen.liu.enmonster;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
//1.获取某个方法的反射
public class Test{
public static void main(String []args){
//要获取print(int,int)方法
//要获取一个方法就是要获取类的信息,获取类的信息首先就是要
//获取类的
A a1 = new A();
Class c = a1.getClass();
//2.获取方法 名称 和参数列表来决定
//getMethod获取的是public方法
//getDeclaredMethod自己声明的方法
try {
Method m = c.getMethod("print",new Class[]{int.class,int.class});
//Method m = c.getMethod("print",int.class,int.class);效果同上
//方法的反射操作
//a1.print(10,20);
//方法的反射操作,是用m对象来进行方法调用 和a1.print(10,20)效果相同
try {
//方法如果没有返回值,返回null,有返回值则返回具体的返回值
Object o = m.invoke(a1,new Object[]{10,20});
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
}
}
class A{
public void print(int a ,int b){
System.out.println(a+b);
}
public void print(String a,String b){
System.out.println(a.toUpperCase()+","+b.toUpperCase());
}
}
- 利用反射,获取Method集合
package shen.liu.enmonster;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
/***
* 1.通过class,Method来认识泛型的本质
*/
public class Test {
public static void main(String []args){
ArrayList list = new ArrayList();
ArrayList<String> list1 =new ArrayList<String>();
list1.add("hello");
Class c1 = list.getClass();
Class c2 = list1.getClass();
System.out.println(c1==c2);
//反射的操作 都是编译之后的操作
/**
* 1.c1 == c2结果返回true ,说明编译之后集合的泛型是去泛型化的
* 2.Java中集合的泛型,是防止错误输入的,只在编译阶段有效
* 绕过编译就无效了
* 3.验证:通过方法的反射来操作,绕过编译
*/
try {
Method m = c1.getMethod("add", Object.class);
try {
m.invoke(list1,Object.class);
//观察是否是加入到了list1中
System.out.println(list1.size());
//此时不能使用for遍历输出 -->类型错误
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
}
}
相关文章
- Java反射 Class类常用方法详解
- java random函数用法_JAVA的Random类的用法详解[通俗易懂]
- java 图片识别 tess4j_JAVA使用Tess4J进行ocr识别
- java源程序文件扩展名_JAVA源代码的扩展名为( )
- java有什么作用_Java有什么用「建议收藏」
- java与python-如何对比Python和Java,只需三分钟告诉你!
- java平均的随机数_Java 随机数详解「建议收藏」
- java oracle数据备份_Java实现Oracle数据库备份
- Java递归详解_java难不难学
- java事务_Java 事务详解[通俗易懂]
- 反编译Java_java反编译的代码可以修改么
- Java反射改变Android属性详解手机开发
- Java反射机制学习详解手机开发
- java基础之反射机制详解编程语言
- Java – 如何通过反射调用对象的方法详解编程语言
- 简单理解Java反射机制详解编程语言
- 在 Java 的反射中,Class.forName 和 ClassLoader 的区别详解编程语言
- java学习笔记09–反射机制详解编程语言
- 服务器使用Java进行Linux服务器监控(java监控linux)
- 策略解析Java中Redis的超时策略(redisjava过期)