一、什么是反射?
JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法,对于任意一个对象,都能够调用它的任意一个方法和属性,这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。
二、什么是 java 序列化?实现对象序列化需要做哪些工作?什么情况下需要序列化?
**序列化:**将 Java 对象转换成字节流的过程。
**反序列化:**将字节流转换成 Java 对象的过程。
JAVA提供了API实现了对象的序列化和反序列化的功能,使用这些API时需要遵守如下约定:
1. 被序列化的对象类型需要实现序列化接口,此接口是标志接口,没有声明任何的抽象方法,JAVA编译器识别这个接口,自动的为这个类添加序列化和反序列化方法。
2. 为了保持序列化过程的稳定,建议在类中添加序列化版本号。
3. 不想让字段放在硬盘上就加transient
在不需要序列化的属性前添加transient,序列化对象的时候,这个属性就不会被序列化。
当 Java 对象需要在网络上传输 或者 持久化存储到文件中时,就需要对 Java 对象进行序列化处理。
以下情况需要使用 Java 序列化:
1. 想把的内存中的对象状态保存到一个文件中或者数据库中时候;
2. 想用套接字在网络上传送对象的时候;
3. 想通过RMI(远程方法调用)传输对象的时候。
三、怎实现序列化?
**序列化的实现:**类实现 Serializable 接口,这个接口没有需要实现的方法。实现 Serializable 接口是为了告诉 jvm 这个类的对象可以被序列化。
注意事项:
- 某个类可以被序列化,则其子类也可以被序列化
- 声明为 static 和 transient 的成员变量,不能被序列化。static 成员变量是描述类级别的属性,transient 表示临时数据
- 反序列化读取序列化对象的顺序要保持一致
package constxiong.interview;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
/**
* 测试序列化,反序列化
*/
public class TestSerializable implements Serializable {
private static final long serialVersionUID = 5887391604554532906L;
private int id;
private String name;
public TestSerializable(int id, String name) {
this.id = id;
this.name = name;
}
@Override
public String toString() {
return "TestSerializable [id=" + id + ", name=" + name + "]";
}
@SuppressWarnings("resource")
public static void main(String[] args) throws IOException, ClassNotFoundException {
//序列化
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("TestSerializable.obj"));
oos.writeObject("测试序列化");
oos.writeObject(618);
TestSerializable test = new TestSerializable(1, "ConstXiong");
oos.writeObject(test);
//反序列化
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("TestSerializable.obj"));
System.out.println((String)ois.readObject());
System.out.println((Integer)ois.readObject());
System.out.println((TestSerializable)ois.readObject());
}
}
四、动态代理是什么?有几种动态代理??有哪些应用???
动态代理:当我们需要给某个类或者接口中的方法添加一些额外的功能比如日志、事务的时候,可以通过创建一个代理类来实现这些功能;该代理类既包含了原有类的完整功能,同时在这些功能的基础上添加了其他的逻辑。这个代理类不是事先定义好的,而是动态生成的,比较灵活;
两种种动态代理:
java动态代理:java动态代理有个缺点就是要被代理的类必须实现一个接口,否则没法代理
cglib动态代理:cglib动态代理可以对没有实现接口的类进行代理
动态代理的应用:AOP
五、怎么实现动态代理?
java的动态代理Demo:
编写一个常规方法的接口,这里我们定义一个用户接口,并且编写保存用户方法save()
/**
* 用户接口
*/
public interface UserDao {
public void save();//定义保存用户的方法
}
编写用户接口的实现类,并且实现接口方法save()
/**
* 用户接口实现类
*/
public class UserDaoImp implements UserDao {
@Override
public void save() {
System.out.println("保存用户");
System.out.println("保存成功啦");
}
}
编写自己的MyInvocationHandler实现InvocationHandler接口,重写invoke方法来添加我们需要添加的额外功能
public class MyInvocationHandler implements InvocationHandler {
//被动态代理的对象
private Object target;
public MyInvocationHandler(Object target) {
this.target = target;
}
/**
* 这个代理类到底是多了什么额外功能
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("开始记录日志,方法开始执行...");
method.invoke(target,args);
System.out.println("结束记录日志,方法结束执行...");
return null;
}
}
通过Proxy来生成代理对象,有俩种方式
public static void main(String[] args) throws Exception {
/**
* 方式一
*/
//通过调用动态代理类Proxy的newProxyInstance()生成代理对象
UserDao userDao= (UserDao) Proxy.newProxyInstance(UserDao.class.getClassLoader(),
new Class<?>[] { UserDao.class }, // 被代理类的接口
new MyInvocationHandler(new UserDaoImp()));//要被代理的对象
userDao.save();
/**
* 方式二
*/
//编写invocationhandler实现额外功能
InvocationHandler handler = new MyInvocationHandler(new UserDaoImp());
//通过调用动态代理类Proxy的getProxyClass()方法获取要代理的类
Class<?> proxyClass = Proxy.getProxyClass(UserDao.class.getClassLoader(), UserDao.class);
//通过代理类生成代理对象
UserDao userDao1 = (UserDao) proxyClass.getConstructor(InvocationHandler.class).
newInstance(handler);
userDao1.save();
}
六、Java反射机制的作用
- 在运行时判断任意一个对象所属的类
- 在运行时构造任意一个类的对象
- 在运行时判断任意一个类所具有的成员变量和方法
- 在运行时调用任意一个对象的方法
七、如何使用Java的反射?
例子
首先,定义一个 Person 类及 Student 类,Student 继承自 Person 类,如下:
package com.test;
public class Person<T> {
public String weight;
public String height;
public Person() {
super();
}
public Person(String weight, String height) {
super();
this.weight = weight;
this.height = height;
}
public String getWeight() {
return weight;
}
public void setWeight(String weight) {
this.weight = weight;
}
public String getHeight() {
return height;
}
public void setHeight(String height) {
this.height = height;
}
@Override
public String toString() {
return "Person [weight=" + weight + ", height=" + height + "]";
}
}
package com.test;
import java.io.Serializable;
public class Student extends Person<String> implements Serializable, Runnable {
public String stuNo;
private String stuName;
public Student() {
super();
}
public Student(String stuNo, String stuName) {
super();
this.stuNo = stuNo;
this.stuName = stuName;
}
public String getStuNo() {
return stuNo;
}
public void setStuNo(String stuNo) {
this.stuNo = stuNo;
}
public String getStuName() {
return stuName;
}
public void setStuName(String stuName) {
this.stuName = stuName;
}
private int exam(String str, Integer tag) throws NoSuchMethodException {
System.out.println(str);
return tag;
}
@Override
public String toString() {
return "Student [stuNo=" + stuNo + ", stuName=" + stuName + "]";
}
@Override
public void run() {
}
}
有了这两个类,我们就可以开始利用反射来获取类的内部结构了。我们先常规的创建对象操作:
@Test
public void test() {
Student student = new Student();
student.setStuNo("01");
student.setStuName("张三");
}
反射的4种获取方式,反射的源头就是获取到一个 Class 对象进行操作类的内部方法和获取类的结构。
注意:父类中声明为 public 的变量、方法、接口等也可以被获取到。
@Test
public void test() throws Exception {
/** 第一种反射方式 */
Class clazz1 = new Student().getClass();
/** 第二种反射方式 */
Class clazz2 = Student.class;
/** 第三种反射方式 */
// 先声明 xxx 类所在包的完整名
String className = "com.test.Student";
Class clazz3 = Class.forName(className);
/** 第四种反射方式 */
Class clazz4 = this.getClass().getClassLoader().loadClass(className);
}
获取类中的变量,并进行赋值
@Test
public void test() throws Exception {
Class clazz = Student.class;
Student student = (Student) clazz.newInstance();
/** 声明为 public 类型的变量可以这样获取 **/
Field field1 = clazz.getField("stuNo");
field1.set(student, "01");
System.out.println(student);
/** 其他类型变量只能通过如下获取 **/
Field field2 = clazz.getDeclaredField("stuName");
field2.setAccessible(true);
field2.set(student, "张三");
System.out.println(student);
}
获取变量的权限修饰符(private、protected、public)
@Test
public void test() throws Exception {
Class clazz = Student.class;
Student student = (Student) clazz.newInstance();
Field field1 = clazz.getField("stuNo");
Field field2 = clazz.getDeclaredField("stuName");
/** 获取 权限修饰符 **/
String str = Modifier.toString(field1.getModifiers());
System.out.println(str);
String str2 = Modifier.toString(field2.getModifiers());
System.out.println(str2);
}
八、Java反射机制的作用
- 在运行时判断任意一个对象所属的类
- 在运行时构造任意一个类的对象
- 在运行时判断任意一个类所具有的成员变量和方法
- 在运行时调用任意一个对象的方法
九、反射机制的优缺点?
- 优点:可以动态执行,在运行期间根据业务功能动态执行方法、访问属性,最大限度发挥了java的灵活性。
- 缺点:对性能有影响,这类操作总是慢于直接执行java代码。
十、反射中,Class.forName()和 ClassLoader.loadClass() 的区别?
Class.forName(className)方法,内部实际调用的方法是 Class.forName(className,true,
classloader);第 2 个 boolean 参数表示类是否需要初始化,Class.forName(className)默认是需要初始化,一旦初始化,就会触发目标对象的 static 块代码执行,static 参数也也会被再次初 始 化 ,ClassLoader.loadClass(className) 方 法 , 内 部 实 际 调 用 的 方 法 是ClassLoader.loadClass(className,false);第 2 个 boolean 参数,表示目标对象是否进行链接,false 表示不进行链接,
由上面介绍可以,不进行链接意味着不进行包括初始化等一些列步骤,那么静态块和静态对象就不会得到执行