目录
一、作用域
作用域 | 同类 | 同包 | 子类 | 不同包 |
public | √ | √ | √ | √ |
protected | √ | √ | √ | ❌ |
friendly | √ | √ | ❌ | ❌ |
private | √ | ❌ | ❌ | ❌ |
二、Java创建对象的方式
1. 通过new语句实例化一个对象
2. 通过反射机制创建对象
3. 通过clone()方法创建一个对象
4. 通过反序列化的方式创建对象
三、反射机制
反射的核心是JVM在运行时才动态加载类或调用方法/访问属性,他不需要事先(写代码的时候或编译期)知道运行对象是谁。
反射机制提供的功能主要有:
(1)得到一个对象所属类
(2)获得一个类的所有成员变量和方法
(3)在运行时创建对象
(4)在运行时调用对象的方法
1. 通过反射得到Class类的三种方式
public class ClassTest {
public static void main(String[] args) {
// 方法1
Student stu = new Student();
Class c1 = stu.getClass();
System.out.println(c1.getName());
// 方法2
Class c2 = Student.class;
System.out.println(c1 == c2);
// 方法3
try {
Class c3 = Class.forName("JavaBase.Student");
System.out.println(c1 == c3);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
// JavaBase.Student
// true
// true
通常情况下,会使用第三种方式获取类信息,原因如下:第一种已经创建出了Student实例,已经失去了反射的意义;第二种需要导入相应类的包,不导包就会编译错误;第三种可以使用字符串,也可以将该字符串写在配置文件中。
2. 通过反射获取构造器
package JavaBase;
import java.lang.reflect.Constructor;
public class ClassTest {
public static void main(String[] args) throws Exception {
Class cls = Class.forName("JavaBase.Student");
System.out.println("********所有公有构造器********");
Constructor[] conArr = cls.getConstructors(); // 获取所有公有构造器
for(Constructor c : conArr){
System.out.println(c);
}
System.out.println("********所有构造器********");
conArr = cls.getDeclaredConstructors(); // 获取所有构造器包括私有、受保护等修饰符修饰的
for(Constructor c : conArr){
System.out.println(c);
}
System.out.println("********所有公有、无参构造器********");
// 因为是无参构造器,所以类型是null,也可以不写(括号里填的是参数类型)
// 返回的是描述这个无参构造器的类对象
Constructor constructor = cls.getConstructor(null);
System.out.println("constructor = " + constructor);
Object obj = constructor.newInstance(); // 调用构造器,创建对象
Student student = (Student) obj;
student.setAge(18);
System.out.println(student.getAge());
System.out.println("********所以私有构造器,并调用********");
constructor = cls.getDeclaredConstructor(String.class);
System.out.println(constructor);
constructor.setAccessible(true); // 暴力访问(忽略访问修饰符)
obj = constructor.newInstance("Jay");
obj.toString();
}
}
class Student{
private String name;
private int age;
public char sex;
public Student(String name, int age) {
this.name = name;
this.age = age;
System.out.println("这是一个有参构造器");
}
public Student() {
System.out.println("这是一个无参构造器");
}
private Student(String name){
this.name = name;
System.out.println("这是一个私有构造器");
}
protected Student(int age) {
this.age = age;
System.out.println("这是一个受保护的构造器");
}
public char getSex() {
return sex;
}
public void setSex(char sex) {
this.sex = sex;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
3. 获取成员变量并调用
public class ClassTest {
public static void main(String[] args) throws Exception {
Class cls = Class.forName("JavaBase.Student");
System.out.println("********所有公有字段********");
Field[] fields = cls.getFields(); // 获取字段
for(Field f : fields){
System.out.println(f);
}
System.out.println("********所有字段********");
fields = cls.getDeclaredFields(); // 获取所有字段包括私有、受保护等修饰符修饰的
for(Field f : fields){
System.out.println(f);
}
System.out.println("********所有公有字段,并调用********");
Field f = cls.getField("sex");
System.out.println(f);
Object obj = cls.getConstructor().newInstance(); // 调用构造器,创建对象
Student student = (Student) obj;
f.set(obj,'男'); // 为sex这个属性赋值
System.out.println(student.sex);
System.out.println("********所以私有字段,并调用********");
f = cls.getDeclaredField("name");
f.setAccessible(true);
f.set(obj,"Jay");
System.out.println(student);
}
}
4. 通过反射获取成员方法
原理同上,只是调用的方法和返回值类型不同
Method[] methods = cls.getMethods();
Method method = cls.getMethod("newName",String.class);
method = cls.getDeclaredMethod("newAge", int.class);
5. 反射的应用场景
1. 逆向代码,如反编译
2. 与注解相结合,如Retrofit
3. 单纯的反射机制应用框架,如EventBus 2.x
4. 动态生成类框架,如Gson
Java的反射机制在平时的业务开发过程中很少使用到,但是在一些基础框架的搭建上应用非常广泛