反射的核心类(都在 java.lang.reflect 和 java.lang 包中)
什么是包(Package)?
包是Java中一种逻辑分组机制,类似于一个文件夹。Java中的类、接口都可以放在包中,多个相关的类/接口可以放在同一个包里。
package com.example.student;
public class Student {
// 类的实现
}
如,上面的student类就属于com.example.student包。
包的分类
-
内置包(Java API)
-
java.lang
:基本类,如String
、Math
、Object
(默认导入) -
java.util
:集合框架、日期、工具类 -
java.io
:输入输出相关类 -
java.net
:网络编程相关类
-
用户自定义包
可以自己创建包,讲类组织起来:
package myapp.utils;
public class Helper {
// 辅助方法
}
包的命名规则
-
通常以反域名的方式命名,如:
com.example.project
-
使用小写字母
-
避免使用Java的保留字
注意:Java源文件的包名,一定要和文件的目录结构保持一致!!!
假设我写了一个类:
package com.example.utils;
public class Tool {
// 工具类
}
那么它的文件就必须放在下面目录结构中:
项目目录/
└── com/
└── example/
└── utils/
└── Tool.java
当javac Tool.java被编译后的字节码Tool.class也会被放在:
com/
└── example/
└── utils/
└── Tool.class
如果你把上面的 Tool.java
放在根目录下,而它的包名还是 com.example.utils
,那么编译时就会报错:
Tool.java:1: 错误: 程序包 com.example.utils 不存在
package com.example.utils;
除非你自己用命令行指定好 classpath 和目录结构,但一般来说,目录结构必须与包名完全对应。
使用包(import)
- 导入单个类
import java.util.ArrayList;
- 导入整个包
import java.util.*;
- 无需导入的包
java.lang
包是默认导入的,比如 String
, System
, Math
等类可以直接使用。
如何创建和使用自定义的包
在 animals 包中加入一个接口(interface):
/* 文件名: Animal.java */
package animals;
interface Animal {
public void eat();
public void travel();
}
在同一个包中加入该接口的实现:
package animals;
/* 文件名 : MammalInt.java */
public class MammalInt implements Animal{
public void eat(){
System.out.println("Mammal eats");
}
public void travel(){
System.out.println("Mammal travels");
}
public int noOfLegs(){
return 0;
}
public static void main(String args[]){
MammalInt m = new MammalInt();
m.eat();
m.travel();
}
}
什么是反射?
反射是 Java 提供的一种机制,它允许程序在运行时查看和操作类的结构。
反射能干什么
功能 | 说明 |
---|---|
获取类的信息 | 包括类名、包名、继承关系、修饰符等 |
获取成员 | 字段(Field)、方法(Method)、构造器(Constructor) |
动态创建对象 | 通过构造器或无参构造方法创建 |
动态调用方法 | 运行时决定调用哪个方法 |
修改字段值 | 哪怕是私有字段,也能访问和修改 |
反射的核心类(都在 java.lang.reflect
和 java.lang
包中)
类/接口 | 用途 |
---|---|
Class<T> | 表示类或接口本身,是反射的入口 |
Field | 表示类中的字段(成员变量) |
Method | 表示类中的方法 |
Constructor | 表示类中的构造函数 |
Modifier | 提供修饰符信息,如 public/private |
反射的常见用法
-
获取Class对象的三种方式
// 方法1:通过类名.class
Class<?> clazz1 = Person.class;
// 方法2:通过对象实例
Person p = new Person();
Class<?> clazz2 = p.getClass();
// 方法3:通过全类名
Class<?> clazz3 = Class.forName("com.example.Person");
-
获取类名、包名、父类、接口
System.out.println(clazz1.getName()); // 类的全名
System.out.println(clazz1.getPackage()); // 包
System.out.println(clazz1.getSuperclass()); // 父类
System.out.println(Arrays.toString(clazz1.getInterfaces())); // 实现的接口
-
获取字段 并访问私有字段
Field field = clazz1.getDeclaredField("name");
field.setAccessible(true); // 关闭访问检查
field.set(p, "张三"); // 给 p 的 name 字段赋值
System.out.println(field.get(p)); // 获取 p 的 name 字段
-
获取方法并调用
Method method = clazz1.getDeclaredMethod("sayHello", String.class);
method.setAccessible(true); // 可访问私有方法
method.invoke(p, "小明"); // 调用方法,参数是对象和实际参数
-
获取构造器并创建对象
Constructor<?> constructor = clazz1.getConstructor(String.class, int.class);
Object obj = constructor.newInstance("李四", 25);
反射的缺点
-
性能较低:反射涉及大量的运行时操作,性能开销比直接代码大。
-
安全问题:可以访问私有字段,破坏封装。
-
不易维护:反射代码可读性差,易出错。