什么叫做类的类?如何获取私有的方法?Java反射机制太强大了,详解Java反射机制【Java养成】
作者:互联网
Java学习打卡:第二十二天
内容导航
Java养成计划(打卡第22天)
内容管理
排除学业过于繁忙的时间还有昨天娱乐停更,我们的分享文章已经进行到了22天,再前面的时间里我们一直再扎实java SE的基础,当然这些基础是非常重要的,今天和大家分享的是Java的反射机制,当然这个模块的内容是十分庞大的,我们会慢慢分享
我们开始今天的内容:Java反射机制
Java反射机制
问题引入—数据库介绍
在说反射机制之前,我们知道计算机将数据存储在数据库里面,数据库就像是一个仓库一样,我们常见的数据库是Oracle,MySQL,DB2,SQLSever,SQLLite -----关系型数据库;Redis,MongoDB,HBase-----非关系型数据库
大概分析一下
- SQLLite ,是Apache旗下的一款微型数据库,它所存储的数据非常小,占用内存也很小,相比Oracle就很小,它的应用场景就是比如手机等嵌入式设备,在实际开发中用到的比较少
- DB2,是IBM旗下的数据库,市场占有率不高,主要应用于金融领域,所以开发中还是使用的比较少
- SQLServer:微软旗下数据库,SQLsever因此与C/C++的兼容性更高,在Java中使用就需要做很多准备工作,所以在java中也很少使用
- MySQL:也是Apache,是轻量级,并且是免费的,所以很多公司都会使用
- Oracle:正量级,存储量庞大,但是会收费,并且所占内存大
现在的问题是,一个公司随着业务的扩大需要将所有与MySQL绑定的Java业务全部换成存储量更大的Oracle,你发现java业务中一共几千个文件,那难道要累死累活几天一个个点开文件,将所有的MySOL改成Oracle? 那效率太低下了
集群:一堆完成同一功能的服务器搭建的架构
比如现在用50台服务器作为数据库存储,那用MySOL又划算了,像再把Oracle换成MySOL,这时的文件规模估计又大了许多,所以再点开文件一个一个修改就不可行了
实际上这个绑定不是直接绑定的,在实际开发中往往是采用了Strategy模式,面向接口编程,既然有很多数据库类型,那就不要直接定义成普通类
数据处理部分Service -----操作数据库接口Dao(定义了所有操作数据库的方法)----Dao的具体实现(mySQLDao)-------MySOL
我们在service中就会定义接口的实现类 Dao d = new OracleDao();上转型变量,实现多态,所以这就是所谓的绑定,我么如果真修改就是将所有代码中的子类对象给修改了
这就是 耦合–两个模块之间相互关联
所以说耦合在实际开发中要解耦
那怎么解耦呢,我们可以定义一个文件dao.properties,在这个文件中我们写入dao = MySOLDao;
我们调用实现类对象时就不直接创建对象了,我们就直接读取dao.proprties文件,根据文件内容来创建实现类对象
那这里我们读取到的是文件中的字符串,如何根据字符串来判定我们到底创建哪类对象呢?这就利用到了反射机制
反射机制就是将文件中的内容映射成类
Java反射的介绍
万物皆可对象
我们知道Java文件编译后称为字节码的文件,那这个字节码就可以抽象成类,而字节码是由类得到的,所以我们就可以抽象出
- Class -------代表类的类
- Package-------代表包的类
- Method ------代表方法的类
- Filed-------代表属性的类
- Constructor-----代表构造方法的类
- Annotation------代表注解的类
反射:在获取这个类的字节码的基础上来解剖这个类
我们既然知道这是类,怎么使用,如果直接创建对象 new,那么是会报错的,因为这个位于lang包的类代表的是类的类,所以它创建出的对象都是代表一个具体的类,new出来的对象是没有意义的,那我们要如何使用class
class的使用
class是对字节码的抽象,那么某一个具体的类的字节码是不是就是Class的实例码呢,那我们就直接定义一个Class变量,之后将某个具体的类的字节码赋值,那就可以得到具体的类了
public class Luogu extends Thread{
public static void main(String[] args) {
Class<String> stz = String.class;//接受类的字节码,这里<>里和后面都是某个具体的类
System.out.println(stz);
}
}
得到的结果就是
class java.lang.String
这里就获取到String类的字节码了
那接口可以吗,比如传入List;
也可以的
class java.awt.List //这里它调取了另外一个List
public class Luogu extends Thread{
public static void main(String[] args) {
Class<Listener> stz= Listener.class;
System.out.println(stz);
}
}
interface java.net.http.WebSocket$Listener
那除了类和接口,那数组可不可以获取字节码呢也是可以的
Class<int[]> stz = int[].class;
得到的结果是
class [I
从这个结果我们可以看出数组也是一个类,所以数组变量也是和其他类的一样都是对象变量
我们获取Class对象–实例码的方式
- 像上面的演示一样利用.class的方式
- 利用Object类里面的getClass() 对象.getClass
Object o = "abs";
Class<Object> stz = o.getClass();//得到对象所对应的实例类的字节码
- (上面两种方式都需要给到具体的类和对象才能知道实例码)
我们其实还可以**利用Class的forName方法来获取,直接通过字符串就能获取到字节码
Class<String> stz = (Class<String>)Class.forName("Date");
这里我们要求的式前后相同,所以要使用一个强制类型转换
但是会出现异常,因为直接给个类型,它找不到的
这里的解决办是我们直接给全路径
public static void main(String[] args) throws ClassNotFoundException {
@SuppressWarnings("unchecked")
Class<String> stz = (Class<String>)Class.forName("java.util.Date");
System.out.println(stz);
}
运行之后就可以打印出类所对应的字节码了
class java.util.Date
由字符串产生类和对象
- 我们先利用Class的forname就可以得到一个字符串所对应的类
- 之后利用存储类的字节码的变量使用.newInstance()方法,就可以按照无参的构造方法创建一个该类的一个实例对象,用一个该变量去接受(但是这里只能就使用无参的构造方法) 那我们要使用含参的构造方法就要使用上面的C欧尼structor类
Class<String> cla = (Class<String>)Class.forName("java.lang.String");
String str = Cla.newInstane();
这里我们如果正常创建一个Integer对象
Integer in = new Integer(5);
将对像设置为5;那我们就不能使用上的无参的newInstance了,这里就要使用Constructor了
Class<Integer> stz = (Class<Integer>)Class.forName("java.lang.Integer");Constructor<Integer> c = stz.getConstructor(int.class); //获取到了其构造方法Integer in = c.newInstance(5);//由构造方法初始化对象System.out.println(in);
这和上面的语句是等价的,这里我们就是将抽象方法也可以由字节码给反射,但是只能获取到非public的方法
那我们如何获取到非public的构造方法,这时就使用getDeclareConstructor();就可以获取了,传入所需要参数的字节码,就可以构造成功;
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, SecurityException{ Class<String> stz = (Class<String>)Class.forName("java.lang.String"); Constructor<String> c = stz.getDeclaredConstructor(char[].class,boolean.class); //Integer in = c.newInstance(5); c.setAccessiable(true);//暴力破解,将非法变为合法 System.out.println(c.newInstance(new char[]{'a'}),true); }
使用该方法就不用区分构造方法的访问权限,不管是public还是非public都可以使用
也就是说类中的访问权限修饰符在反射面前是无效的
获取一个类的所有构造方法的字节码
我们还可以直接使用getDclareConstructors获取类的所有构造方法;
比如String类的
@SuppressWarnings("rawtypes") public static void main(String[] args) { Class<String> stz = String.class; Constructor[] sc = stz.getDeclaredConstructors(); for(Constructor c:sc)//使用的for-each循环 { System.out.println(c); } }
输出的结果是
public java.lang.String(byte[])
public java.lang.String(byte[],int,int)
public java.lang.String(byte[],java.nio.charset.Charset)
public java.lang.String(byte[],java.lang.String) throws java.io.UnsupportedEncodingException
public java.lang.String(byte[],int,int,java.nio.charset.Charset)
java.lang.String(char[],int,int,java.lang.Void)
java.lang.String(java.lang.AbstractStringBuilder,java.lang.Void)
public java.lang.String(java.lang.StringBuilder)
public java.lang.String(java.lang.StringBuffer)
java.lang.String(byte[],byte)
public java.lang.String(char[],int,int)
public java.lang.String(char[])
public java.lang.String(java.lang.String)
public java.lang.String()
public java.lang.String(byte[],int,int,java.lang.String) throws java.io.UnsupportedEncodingException
public java.lang.String(byte[],int)
public java.lang.String(byte[],int,int,int)
public java.lang.String(int[],int,int)
这就将String类的所有构造方法给打印出来了
获取一个类的方法(也可以忽略访问权限)
Method m = stz.getDeclaredMethod(“charAt”,int.class);
char c = (char) m.invoke(str,5);
反射的总结
上面杂七杂八说了很多,总的来说,反射就是我们可以通过字节码的方式反射出相应的类,方法,构造方法,属性等
-
我们的获取类的字节码的方式有三种,一种是直接类名.class;还有就是由具体的对象获取 Object obj; obj.getClass就可以获取字节码;还有就是使用class类的forName方法,但是这种方法我们需要注意就是要将类名加在<>里,还有就是要给完全路径
-
我们获取到类的字节码,我们就可以根据字节码获取到相关的Constructor,Method对象, 比如存储字节码的是stz, 那么通过stz.getDeclaredConstructor()就可以获取到相关的含参构造方法,对其实现就newInstance就可以使用
-
同理我们使用方法也可以根据stz,stz.getDeclaredMethod()就可以获取方法,只是在获取方法时要传入参数的字节码,比如int.class ,String.class;实现就invoke()—传入具体的参数就可
-
最关键的就是我们获取的方法都是不区分访问权限的,以前普通的方法或许存在不可见的问题,但是反射直接可以暴力破解 setAccessiable(true);这样我们就可以获取到私有的方法了
好了,今天的分享就到这里,感觉有些东西没有说清楚,明天会继续分析,望海涵~~
标签:lang,反射,Java,String,太强大,class,Class,java,public 来源: https://blog.csdn.net/a23452/article/details/120557585