文章目录
Java反射面试
在运行状态中,对于任意一个类,都能知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个属性和方法。依靠此机制,可以动态的创建一个类的对象和调用对象的方法。
其次就是反射相关的API,只讲一些常用的,比如获取一个Class对象。Class.forName(完整类名)。通过Class对象获取类的构造方法,class.getConstructor。根据class对象获取类的方法,getMethod和getMethods。使用class对象创建一个对象,class.newInstance等。
最后可以说一下反射的优点和缺点,优点就是增加灵活性,可以在运行时动态获取对象实例。缺点是反射的效率很低,而且会破坏封装,通过反射可以访问类的私有方法,不安全。
如果了解JVM可以结合JVM的相关知识说。
- 反射可以在程序运行期间加载、探知和使用编译期间完全未知的类。
获取Class类的实例
public class Test02 {
public static void main(String[] args) throws ClassNotFoundException {
Person person =new Student();
System.out.println("这个人是:"+person.name);
//方式一:通过对象获取
Class c1=person.getClass();
//方式二:forname获取
Class c2=Class.forName("Student");
System.out.println(c2.hashCode());
//方式三:通过.class获得
Class c3 = Student.class;
System.out.println(c3.hashCode());
//方式四:基本内置类型的包装类都有一个Type属性
Class c4=Integer.TYPE;
System.out.println(c4);
//获得父类类型
Class c5=c1.getSuperclass();
System.out.println(c5);
}
}
class Person{
public String name;
public Person(){
}
public Person(String name) {
this.name = name;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
'}';
}
}
class Student extends Person{
public Student(){
this.name="学生";
}
}
class Teacher extends Person{
public Teacher(){
this.name="老师";
}
}
那些类可以有Class对象?
class:外部类,成员(成员内部类,静态内部类),局部内部类,匿名内部类。
interface:接口
[]:数组
enum:枚举
annotation:注解@interface
primitive type:基本数据类型
void
public class Test03 {
public static void main(String[] args) {
Class c1=Object.class;//类
Class c2=Comparable.class;//接口
Class c3=String[].class;//一维数组
Class c4=int[][].class;//二位数组
Class c5=Override.class;//注解
Class c6= ElementType.class;//枚举
Class c7=Integer.class;//基本数据类型
Class c8=void.class;//void
Class c9=Class.class;//Class
//快速换行复制ctrl+D
//多行选择复制alt
System.out.println(c1);
System.out.println(c2);
System.out.println(c3);
System.out.println(c4);
System.out.println(c5);
System.out.println(c6);
System.out.println(c7);
System.out.println(c8);
System.out.println(c9);
//只要元素类型与维度(一维数组跟多维数组)一样,就是同一个Class.
int[] a=new int[10];
int[] b=new int[100];
System.out.println(a.getClass().hashCode());
System.out.println(b.getClass().hashCode());
}
}
Java内存分析
类加载器
public static void main(String[] args) throws ClassNotFoundException {
//获取系统类的加载器
ClassLoader systemClassLoader=ClassLoader.getSystemClassLoader();
System.out.println(systemClassLoader);
//获取系统类加载器的父类加载器-->扩展类加载器
ClassLoader parent =systemClassLoader.getParent();
System.out.println(parent);
//获取扩展类加载器的父类加载器-->根加载器(c/c++)
ClassLoader parent1 = parent.getParent();
System.out.println(parent1);
//测试当前类是那个加载器加载的
ClassLoader classLoader=Class.forName("Test04").getClassLoader();
System.out.println(classLoader);
//测试JDK内置的类是那个加载器加载的
classLoader=Class.forName("java.lang.Object").getClassLoader();
System.out.println(classLoader);
//如何获得系统类加载器可以加载的路径
System.out.println(System.getProperty("java.class.path"));
}
获取运行时类的完整结构
Field、Method、Constructor、Superclass、Interface、Annotation
public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, NoSuchMethodException {
Class c1=Class.forName("User");
User user = new User();
c1=user.getClass();
//获得类的名字
System.out.println(c1.getName());//获取包名+类名
System.out.println(c1.getSimpleName());//获得类名
//获得类的属性
Field[] fields=c1.getFields();//只能找到public属性
fields=c1.getDeclaredFields();//找到全部的属性
for(Field field:fields){
System.out.println(field);
}
//获得指定属性的值
Field name =c1.getDeclaredField("name");
System.out.println(name);
System.out.println("***********************");
//获得类的方法
Method[] methods = c1.getMethods();//获得本类及其父类的全部public方法
for(Method method:methods){
System.out.println("正常的:"+method);
}
methods=c1.getDeclaredMethods();//获得本类的所有方法
for(Method method:methods){
System.out.println("getDeclaredMethods:"+method);
}
//获得指定方法
//重载
Method getName=c1.getMethod("getName",null);
Method setName=c1.getMethod("setName",String.class);
//Method setName=c1.getMethod("getName",String.class);
System.out.println(getName);
System.out.println(setName);
//获得指定的构造器
System.out.println("***********************");
Constructor[] constructors = c1.getConstructors();
for (Constructor constructor : constructors) {
System.out.println(constructor);
}
Constructor[] declaredConstructors = c1.getDeclaredConstructors();
for (Constructor declaredConstructor : declaredConstructors) {
System.out.println("#"+declaredConstructor);
}
//获得指定的构造器
Constructor declaredConstructor=c1.getDeclaredConstructor(String.class,int.class,int.class);
System.out.println("指定:"+declaredConstructor);
}
有了Class对象,能做什么?
public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException, NoSuchFieldException {
//获得class对象
Class c1=Class.forName("User");
//构造一个对象
User user = (User) c1.newInstance();//本质是调用的无参构造器
System.out.println(user);
//通过构造器创建对象
Constructor declaredConstrcutor=c1.getDeclaredConstructor(String.class,int.class,int.class);
User user2=(User) declaredConstrcutor.newInstance("张苏杭",001,18);
System.out.println(user2);
//通过反射调用普通方法
User user3=(User)c1.newInstance();//通过反射创建一个对象
//通过反射获取一个方法
Method setName=c1.getDeclaredMethod("setName", String.class);
setName.invoke(user3,"苏杭");//激活invoke(对象,"方法的值")
System.out.println(user3.getName());
//通过反射操作属性
User user4=(User)c1.newInstance();
Field name=c1.getDeclaredField("name");
//不能直接操作私有属性,我们需要关闭程序的安全检测,属性或者方法的name.setAccessible(true)
name.setAccessible(true);//
name.set(user4,"书行");
System.out.println(user4.getName());
}
反射操作注解
要求:
代码:
import java.lang.annotation.*;
import java.lang.reflect.Field;
//练习反射操作注解
public class Test07 {
public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException {
Class c1=Class.forName("Student2");
//通过反射获取注解
Annotation[] annotations = c1.getAnnotations();
for (Annotation annotation : annotations) {
System.out.println(annotation);
}
//获得注解的value的值。
Tablesu annotation1=(Tablesu)c1.getAnnotation(Tablesu.class);
String value = annotation1.value();
System.out.println(value);
//获得类指定的注解
Field f=c1.getDeclaredField("name");
Fieldsu annotation=f.getAnnotation(Fieldsu.class);
System.out.println(annotation.columnName());
System.out.println(annotation.type());
System.out.println(annotation.length());
}
}
@Tablesu("db_student")
class Student2{
@Fieldsu(columnName= "db_id",type = "int",length = 10)
private int id;
@Fieldsu(columnName= "db_age",type = "int",length = 10)
private int age;
@Fieldsu(columnName= "db_name",type = "varchar",length = 10)
private String name;
public Student2() {
}
public Student2(int id, int age, String name) {
this.id = id;
this.age = age;
this.name = name;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Student2{" +
"id=" + id +
", age=" + age +
", name='" + name + '\'' +
'}';
}
}
//类名的注解
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@interface Tablesu{
String value();
}
//属性的注解
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@interface Fieldsu{
String columnName();
String type();
int length();
}
反射使用的场景
- JDBC中,利用反射动态加载了数据库驱动程序。
- Web服务器中利用反射调用了Sevlet的服务方法。
- 很多框架都用到反射机制,注入属性,调用方法,如Spring。
什么叫对象序列化,什么是反序列化,实现对象序列化需要做那些工作?
对象序列化,将对象中的数据编码为字节序列的过程。
反序列化;将对象的编码字节重新反向解码为对象的过程。
JAVA提供了API实现了对象的序列化和反序列化的功能,使用这些API时需要遵守如下约定:
被序列化的对象类型需要实现序列化接口,此接口是标志接口,没有声明任何的抽象方法,JAVA编译器识别这个接口,自动的为这个类添加序列化和反序列化方法。
为了保持序列化过程的稳定,建议在类中添加序列化版本号。
不想让字段放在硬盘上就加transient
以下情况需要使用Java序列化:
想把的内存中的对象状态保存到一个文件中或者数据库中时候;
想用套接字在网络上传送对象的时候;
想通过RMI(远程方法调用)传输对象的时候。