文章目录
反射
反射提供的功能:
- 在运行时判断任意一个对象所属的类
- 在运行时构造任意一个类的对象
- 在运行时判断任意一个类所具有的成员变量和方法
- 在运行时获取泛型信息
- 在运行时调用任意一个对象的成员变量和方法
- 在运行时处理注解
- 生成动态代理
java.lang.Class 代表一个类
java.lang.reflect.Constructor; 构造方法
java.lang.reflect.Field; 属性
java.lang.reflect.Method; 方法
利用反射创建对象
公有属性,方法,构造方法
Class<Person> cla = Person.class;
使用反射创建对象,利用公用构造器
Constructor<Person> con = cla.getConstructor(String.class , int.class , String.class);
Person pe = con.newInstance("猪八戒",20,"濮阳"); 获得Person对象
调属性
Field name = cla.getDeclaredField("locatal");
name.set(pe,"漯河");
调方法
Method getname = cla.getDeclaredMethod("getName");
String myname = getname.invoke(pe);
私有属性,方法,构造方法
Class<Person> cla = Person.class;
构造方法
Constructor<Person> con2 = cla.getDeclaredConstructor();
con2.setAccessible(true);
Person pe2 = con2.newInstance();
属性
Field name = cla.getDeclaredField("name");
name.setAccessible(true);
name.set(pe2,"蔡文姬");
方法
Method setage = cla.getDeclaredMethod("setAge",int.class);
setage.setAccessible(true);
setage.invoke(pe2,18);
System.out.println(pe2.toString());
反射的用处
反射和封装是否矛盾?
不矛盾。看起来好像是矛盾的
封装性的用处是提示:不建议调用私有方法,调用公有方法就够用了
反射解决的是能不能调的问题
创建对象用哪个?
1. 建议用new
2. 但是有时用反射,学习Java web时再解决
Class类的理解
内部解析
- .java文件经过javac.exe之后,会生成字节码文件.class文件,一个Java类对应一个.class文件。
- 然后使用java.exe对某个字节码文件解释运行,加载到内存中。
- 加载到内存中的类叫做运行时类,就是这个Class类。
- 换句话说:Class的实例对应着一个运行时类。
- 我们可以利用这个实现创建新的对象
加载到内存中的运行时类会缓存一定的时间,在此时间内,我们可以用不同方式获取运行时类,获取到的是同一个运行时类
哪些类型具有Class对象?
- class
- interface
- [ ]数组:只要元素类型和维度一样,则是同一个Class
- enum
- annotation
- 基本数据类型
- void
获取Class实例的方式
一:直接属性获得
动态性比较差,当类名写错的时候,编译时就报错
Class<Person> cla = Person.class;
二:通过运行时的类获得
可以用来比较两个对象是不是由同一个类实例化的
Person p1 = new Person();
Class<? extends Person> cla2 = p1.getClass();
三:使用对象名获得
很常用,动态性强,既是类名写错编译时也不会报错
Class cla3 = Class.forName("test.Person");
四:使用类加载器获得
了解一下就行
下面一行的construct指的是当前代码所在的类名
ClassLoader loader = construct.class.getClassLoader();
Class cla4 = loader.loadClass("test.Person");
properties读取配置文件
首先创建一个jdbc.properties文件,和jdbc.properties,一个在modul下,一个在src下
里面的内容写键值对:
user=刘春阳
password=123456
方式一
默认搜索路径是当前module下,eclipse是在当前项目下
Properties pros = new Properties();
FileInputStream inp = new FileInputStream("jdbc.properties");
//可以读取src内的文件
FileInputStream inp1 = new FileInputStream("src\\jdbc1.properties");
pros.load(inp);
String str = pros.getProperty("user");
System.out.println(str);
方式二
默认搜索路径是src下
Properties pros = new Properties();
ClassLoader load = construct.class.getClassLoader();
InputStream imp = load.getResourceAsStream("jdbc.properties");
pros.load(imp);
String str = pros.getProperty("user");
System.out.println(str);
创建运行时类的对象
调用空参构造器
用的比较多,比较常用,但是要确保具有空参构造器
Class cla = Class.forName("test.Person");
Person pe = (Person)cla.newInstance();
.newInstance():默认调用空参构造器,因此使用的时候必须确保具有空参构造器可被调用
调用带参构造器
用的比较少,因为写出来的代码只使用与这一个类,不通用。
Class cla = Class.forName("test.Person");
Constructor<Person> cons = cla.getConstructor(String.class,int.class,String.class);
Person pe = cons.newInstance("后羿",20,"王者峡谷");
举例
外部调用这个函数,传进去对象的路径即可
public Object getInstance(String path) throws Exception{
Class cla = Class.forName(path);
return cla.newInstance();
}
获取运行时类的完整结构
了解即可,项目中基本不用这个
获取属性
获取运行时类及其父类内为public型的属性
Field[] fil = cla.getFields();
for(Field i : fil){
System.out.println(i);
}
获取运行时内内部的所有属性,不包括其父类
Field[] fil2 = cla.getDeclaredFields();
for(Field i : fil2){
System.out.println(i);
}
获取类内部某个属性的指定信息
只是在上面for循环的基础上再调用属性
Field[] fil2 = cla.getDeclaredFields();
for(Field i : fil2){
//获取权限修饰符,返回值为int,需要用toString转换为对应值
int fir = i.getModifiers();
System.out.print(Modifier.toString(fir)+" ");
//获取数据类型,返回的是Class
Class ty = i.getType();
System.out.print(ty+" ");
//获取变量名
System.out.println(i.getName());
}
获取方法
前两步与上面的获取属性一样,将Field变成Method即可
Class cla = Person.class;
Method[] met = cla.getDeclaredMethods();
for(Method i : met)
获取方法的注解
Annotation[] ann = i.getAnnotations();
for(Annotation w : ann)
System.out.println(w);
获取方法权限修饰符
i.getModifiers() 返回的事int型数值,需要用下面的函数进行转换
Modifier.toString()
获取方法返回值
i.getReturnType()
获取方法名
i.getName()
形参方法参数列表
返回的是Class数组
Class[] qwe = i.getParameterTypes();
for(Class w : qwe)
System.out.println(w);
返回抛的异常
也是一个Class数组
Class[] exce = i.getExceptionTypes();
for(Class w : exce)
System.out.println(w);
获取构造方法
获取public修饰的构造器
Constructor[] con = cla.getConstructors();
获取所有的构造器
Constructor[] coms = cla.getDeclaredConstructors();
获取父类
Class cl = cla.getSuperclass();
System.out.println(cl);
获取父类及其泛型
Type cl = cla.getGenericSuperclass(); 返回值为Type类型
System.out.println(cl);
仅获取父类的泛型
Type cl = cla.getGenericSuperclass();
ParameterizedType para = (ParameterizedType) cl;
Type[] tya = para.getActualTypeArguments();
for(Type w : tya)
System.out.println(w);
获取接口
Class[] c = cla.getInterfaces();
for(Class w : c)
System.out.println(w);
获取所在的包
Class cla = Person.class;
Package pac = cla.getPackage();
System.out.println(pac);
获取类的注解
Annotation[] a = cla.getAnnotations();
调用运行时类的指定结构
属性
Class cla = Person.class;
Person pe = (Person)cla.newInstance();
//可以获取一个类的所有格式属性
Field fi = cla.getDeclaredField("name");
//使私有的变量可以用反射进行修改
fi.setAccessible(true);
fi.set(pe,"你好");
System.out.println(fi.get(pe));
下面这个函数只能获取public修饰个属性,所以说不经常用
//获取指定的属性,只能获取public的
Field fi = cla.getField("locatal");
//给某个对象的某个属性赋值
fi.set(pe,"nihao");
System.out.println(fi.get(pe));
System.out.println(pe.toString());
方法
静态方法
静态属性同理,使用任意一个对象调用赋值即可
Class<Person> cla = Person.class;
Person pe = cla.newInstance();
//获取所有权限的方法
Method mttt = cla.getDeclaredMethod("sayhellow");
mttt.setAccessible(true);
String str = (String) mttt.invoke(PerSon.class);括号内也可以设置null,随意一个就行,因为是静态方法
System.out.println(str);
动态方法
//第一个参数指明函数名,后面的参数值函数内的形参类型
Method mea = cla.getDeclaredMethod("setAge",int.class);
mea.setAccessible(true);
//参数1:指定的对象,后面的参数:实参值.方法有返回值,object类型
mea.invoke(pe,19);
System.out.println(pe.getAge());
构造器
一般就是使用class对象直接newInstance()造对象,调用非空构造器不常用
Constructor<Person> con2 = cla.getDeclaredConstructor(String.class , int.class , String.class);
con2.setAccessible(true);
Person pe2 = con2.newInstance("猪八戒",20,"濮阳");
代理模式
静态代理
很简洁明了,但是就是不懂有何用
package ha;
//一个接口,很重要
interface Netwotk{
void browse();
}
//被代理类
class Server implements Netwotk{
@Override
public void browse() {
System.out.println("真实的服务器访问网络");
}
}
//代理类
class ProxyServer implements Netwotk{
private Netwotk work;
//将主函数内的Netwotk对象传进来
public ProxyServer(Netwotk work){
this.work = work;
}
//未知的一些操作,按需所写
public void check(){
System.out.println("联网前的一些操作");
}
@Override
public void browse() {
check();
work.browse(); //调用被代理类内的相关函数
}
}
//主函数
public class staticProxy {
public static void main(String[] args) {
Server se = new Server();
ProxyServer pro = new ProxyServer(se);
pro.browse();
}
}
动态代理
扩展性比静态代理好
package ha;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
interface human{
String getBelief();
void eat(String food);
}
//被代理类
class SuperMan implements human{
@Override
public String getBelief() {
return "I am ironman";
}
@Override
public void eat(String food) {
System.out.println("我喜欢吃:"+food);
}
}
//动态生成被代理类
class ProxyFactory{
//调用此方法,返回代理类的对象
public static Object getProxyInstance(Object obj){ //obj是被代理类对象
MyInvocationHandler hande = new MyInvocationHandler();
hande.bind(obj);
return Proxy.newProxyInstance(obj.getClass().getClassLoader()
,obj.getClass().getInterfaces()
,hande);
}
}
//需要继承指定的接口
class MyInvocationHandler implements InvocationHandler{
private Object obj; //赋值时需要被代理类对象进行赋值
public void bind(Object obj){
this.obj = obj;
}
//当我们通过代理类的对象,调用方法a时,就会自动调用下面的方法
//将被代理类要执行的方法a的功能就声明在invoke()中
@Override //被代理对象 要调用的函数 函数内的参数
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object valuess = method.invoke(obj,args); //函数的返回值
return valuess;
}
}
public class ProxyTest {
public static void main(String[] args) {
SuperMan man = new SuperMan();
//代理类对象,此时就不仅仅局限于human了,上面写的方法是通用的,被代理类可以随意的换成其他的
human proxyInstance = (human) ProxyFactory.getProxyInstance(man);
proxyInstance.eat("王婆大虾");
String str = proxyInstance.getBelief();
System.out.println(str);
}
}