反射机制
通过java语言中的反射机制可以操作字节码文件(可以读和修改字节码文件)。通过反射机制可以操作代码片段。(class文件)。反射机制,让代码很具有通用性,可变化的内容都是写到配置文件当中,将来修改配置文件之后,创建的对象不一样了,调用的方法也不同了,但是java代码不需要做任何改动。
2.3、反射机制相关的重要的类有哪些? java. lang.class:代表整个字节码,代表一个类型 java. lang.reflect.Method:代表字节码中的方法字节码。 java.lang.reflect.constructor:代表字节码中的构造方法字节码。 java.lang . reflect.Field:代表字节码中的属性字节码。
class
获取class的方法
Class.forName()
- 1、静态方法
- 2、方法的参数是一个字符串。
- 3、字符串需要的是一个完整类名。
- 4、完整类名必须带有包名。
try {
Class c=Class.forName("java.util.Date");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
只执行静态代码块
- 如果你只是希望一个类的静态代码块执行,其它代码一律不执行,你可以使用 Class.forName(“完整类名”);这个方法的执行会导致类加载,类加载时,静态代码块执行。
package h1;
public class Test2 {
public static void main(String[] args) {
try {
Class c=Class.forName("h1.Tsst");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
class Tsst{
static {
System.out.println("hello");
}
}
结果:
hello
getClass()
java中任何一个对象都有一个方法:
String s="a";
Class c=s.getClass();
c代表String.class字节码文件,c代表string类型。
扩展:
Class c1=null;
try {
c1=Class.forName("java.lang.String");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
String s="a";
Class c=s.getClass();
System.out.println(c==c1);
结果:
true
字节码文件装载到JVM中的时候只装载一份。也就是说Class.forNname()和getClass()指向的是同一份文件,文件为Class.String文件。
.class属性
java语言中任何一种类型,包括基本数据类型,它都有.class属性。
Class c=String.class;
Class c1=Date.class;
读属性文件实例化对象
- 通过读属性文件实例化对象的代码是灵活的,代码不需要改动,可以修改配置文件,配置文件修改之后,可以创建出不同的实例对象。
创建名为:myClass.properties的File
内容如下
class=h1.Tsst
class Tsst{
static {
System.out.println("helllo");
}
}
public class Test2 {
public static void main(String[] args) throws FileNotFoundException {
FileReader f=new FileReader("D:\\IDEA\\IntelliJ IDEA Community Edition 2020.3.3\\untitled3\\untitled\\src\\myClass.properties");
Properties p=new Properties();
try {
p.load(f);
f.close();
String name=p.getProperty("class");
Class.forName(name);
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
结果:
hello
获取文件绝对路径
通过流的方法来获取路径 FileReader f=new FileReader(“路径”); 这个代码换到了其它位置,可能当前路径就不是project的根了,这时这个路径就无效了。
获取文件绝对路径,通用的一种方式
String path=Thread.currentThread().getContextClassLoader().getResource("路径").getPath();
FileReader f=new FileReader(path);
Properties p=new Properties();
p.load(f)
f.close();
- Thread. currentThread()当前线程对象
- getContextClassloader()是线程对象的方法,可以获取到当前线程的类加载器对象
- getResource() [获取资源] 这是类加载器对象的方法,当前线程的类加载器默认从类的根路径下加载资源
注意:要获取路径的文件必须在类路径下(src下的都是类路径)
以流的形式返回
InputStream is=Thread.currentThread().getContextClassLoader().getResourceAsStream("路径");
Properties p=new Properties();
p.load(is);
is.close();
资源绑定器
class=h1.Tsst
ResourceBundle re=ResourceBundle.getBundle("myClass");
String s=re.getString("class");
System.out.println(s);
类加载器
什么是类加载器 专门负责加载类的命令。(ClassLoader)
3个类加载器
String s = “abc” ; 代码在开始执行之前,会将所需要类全部加载到JVM当中。 通过类加载器加载,看到以上代码类加载器会找string.class 文件,找到就加载
加载顺序 首先通过“启动类加载器”加载。
如果通过“启动类加载器”加载不到的时候,会通过"扩展类加载器”加载。
如果“扩展类加载器"没有加载到,那么会通过"应用类加载器”加载。 注意:应用类加载器专门加载: classpath中的类。
双亲委派机制
java中为了保证类加载的安全,使用了双亲委派机制。优先从启动类加载器中加载,这个称为"父”,若“父"无法加载到,再从扩展类加载器中加载,这个称为母”。双亲委派。如果都加载不到,才会考虑从应用类加载器中加载。直到加载到为止。
Field
获取Field
获取完整类名
Class c=Class.forName("类名");
c.getName();
获取简类名
Class c=Class.forName("类名");
c.getSimpleName();
获取类中所有的public修饰的Field
Field[] f=c.getFields();
Field f1=f[0];
f1.getName();
for(Field fi:f){
System.out.println(fi.getName());
}
获取类中所有的Field
(包括私有属性)
Field[] f=c.getDeclaredFields();
获取属性的类型
public String passwd;
private String username;
Class c=Class.forName("Student");
Field[] f1=c.getFileds();
for (Field fi:f1) {
Class type=fi.getType();
System.out.println(type.getSimpleName());
}
结果:
String
String
获取属性的修饰符
Class c=Class.forName("Student");
Field[] f1=c.getFileds();
for (Field fi:f1) {
int i=fi.Modifiers();
System.out.println(Modifiers.toString(i));
}
结果:
public
private
反编译Field
Class c=Class.forName("UserInformation");
StringBuilder s=new StringBuilder();
s.append(Modifier.toString(c.getModifiers())+" class "+c.getSimpleName()+"{"+"\n");
Field[] f=c.getDeclaredFields();
for (Field fi:f
) {
s.append("\t");
s.append(Modifier.toString(c.getModifiers()));
s.append(" ");
s.append(fi.getType().getSimpleName());
s.append(" ");
s.append(fi.getName());
s.append(";\n");
}
s.append("}");
System.out.println(s);
}
结果:
public class UserInformation{
public String passwd;
public String username;
}
通过反射机制访问(赋值)对象属性的值
Class c=Class.forName("UserInformation");
Object o=c.newInstance();
Field f=c.getDeclaredField("passwd");
System.out.println(f.get(o));
f.set(o,"456");
System.out.println(f.get(o));
结果:
123
456
虽然使用了反射机制,但是三要素还是缺一不可: 1 :o对象 2 :passwd属性 3 :123值 注意:反射机制让代码复杂了,但是为了一个“灵活”,这也是值得的。
私有属性
Field f1=c.getDeclaredField("username");
f1.setAccessible(true);
f1.set(o,"asd");
System.out.println(f1.get(o));
Method
可变长度参数
语法: 类型… ( 注意: 一定是3个点。)
- 1、可变长度参数要求的参数个数是 : 0~N个。
- 2、可变长度参数在参数列表中必须在最后一个位置上,而且可变长度参数只能有1个。
- 3、可变长度参数可以当做一个数组来看待
public static void main(String[] args){
st(1,2,3);
}
public static void st(int...sum){
for (int i = 0; i < sum.length; i++) {
System.out.println(sum[i]);
}
结果:
1
2
3
反射Method
- 方法的访问修饰符
getModifiers() - 方法的返回值类型
getReturnType() - 方法名
getName() - 方法参数的类型
getParameterTypes()
Class c=Class.forName("UserInformation");
Method[] m=c.getDeclaredMethods();
for (Method me:m) {
System.out.println(Modifier.toString(me.getModifiers()));
System.out.println(me.getReturnType().getSimpleName());
System.out.println(me.getName());
Class[] c1=me.getParameterTypes();
for (Class cl:c1) {
System.out.println(cl.getSimpleName());
}
}
反编译Method
Class c=Class.forName("UserInformation");
StringBuilder s=new StringBuilder();
s.append(Modifier.toString(c.getModifiers())+" class "+c.getSimpleName()+"{\n");
Method[] m=c.getDeclaredMethods();
for (Method me:m) {
s.append("\t");
s.append(Modifier.toString(me.getModifiers())+" ");
s.append(me.getReturnType().getSimpleName()+" ");
s.append(me.getName());
s.append("(");
Class[] cl=me.getParameterTypes();
for (Class cla:cl) {
s.append(cla.getSimpleName());
s.append(",");
}
s.deleteCharAt(s.length()-1);
s.append(")");
s.append("{}\n");
}
s.append("}");
System.out.println(s);
}
结果:
public class UserInformation{
public String getPasswd){}
public void setUsername(String){}
public String getUsername){}
public void setPasswd(String){}
}
反射机制调用方法
Class c=Class.forName("UserInformation");
Object o=c.newInstance();
Method m=c.getDeclaredMethod("setPasswd",String.class);
Object retValue=m.invoke(o,"123456");
调用方法四要素: 1.m方法 2.o对象 3."123"实参 4.retValue返回值
反射Constructor
(拼接构造方法)
public UserInformation(String passwd, String username) {
this.passwd = passwd;
this.username = username;
}
Class c=Class.forName("UserInformation");
StringBuilder s=new StringBuilder();
s.append(Modifier.toString(c.getModifiers())+" class "+c.getSimpleName()+"{\n");
Constructor[] con=c.getDeclaredConstructors();
for (Constructor co:con) {
s.append("\t");
s.append(Modifier.toString(co.getModifiers()));
s.append(" ");
s.append(co.getName());
s.append("(");
Class[] cl=co.getParameterTypes();
for (Class cla:cl) {
s.append(cla.getSimpleName());
s.append(",");
}
if(s.length()>0){
s.deleteCharAt(s.length()-1);
}
}
s.append("){}\n");
s.append("}");
System.out.println(s);
结果:
public class UserInformation{
public UserInformation(String,String){}
}
反射机制调用构造方法
public UserInformation(String passwd, String username) {
this.passwd = passwd;
this.username = username;
}
@Override
public String toString() {
return "UserInformation{" +
"passwd='" + passwd + '\'' +
", username='" + username + '\'' +
'}';
}
Class c=Class.forName("UserInformation");
Constructor con=c.getConstructor(String.class,String.class);
Object o=con.newInstance("asd","123456");
System.out.println(o);
结果:
UserInformation{passwd='asd', username='123456'}
调用无参构造方法
public UserInformation() {
}
Constructor con1=c.getConstructor();
Object o1=con1.newInstance();
System.out.println(o1);
结果:
UserInformation{passwd='null', username='null'}
获取父类和父接口
Class c=Class.forName("java.lang.String");
Class c1=c.getSuperclass();
System.out.println(c1.getSimpleName());
Class[] c2=c.getInterfaces();
for (Class cl:c2) {
System.out.println(cl.getSimpleName());
}
结果:
Object
Serializable
Comparable
CharSequence
Constable
ConstantDesc
|