黑马程序员:Java基础总结
反射
反射
Class类代表Java类,它的各个实例对象又分别对应什么呢?
对应各个类在内存中的字节码,例如,Person类的字节码,ArrayList类的字节码,等等。
一个类被类加载器加载到内存中,占用一片存储空间,这个空间里面的内容就是类的字节码,不同的类的字节码是不同的,所以它们在内存中的内容是不同的,这一个个的空间可分别用一个个的对象来表示,这些对象显然具有相同的类型,这个类型是什么呢?
如何得到各个字节码对应的实例对象( Class类型)
类名.class,例如,System.class
对象.getClass(),例如,new Date().getClass()
九个预定义Class实例对象:
参看Class.isPrimitive方法的帮助
Int.class == Integer.TYPE
public
static
void
main(String[] args)
throws
Exception {
for
(Field field : fields) {
// if(field.getType().equals(String.class)){
if
(field.getType() == String.
class
) {
public
String
a
=
"jfaiweonv"
;
public
String
b
=
"fewiojavo"
;
public
String
c
=
"fejwaofkpwel"
;
@Override
public
String toString() {
return
a
+
":"
+
b
+
":"
+
c
;
int
[] a = { 1, 2, 3, 4, 5 };
if
(b.getClass().isArray()) {
public
static
void
main(String[] args)
throws
Exception {
// 应该先直接用ArrayList和HashSet,然后才引入从配置文件读,这样便于学员学习。
// 先演示相对路径的问题
完整路径:Jsp中
相对路径:
// InputStream ips = new FileInputStream("config.properties");
/*
// InputStream ips =
// ReflectTest2.class.getClassLoader().getResourceAsStream("cn/ itcast/javaenhance /config.properties");
// Class提供了一个便利方法,用加载当前类的那个类加载器去加载相同包目录下的文件
// InputStream ips =
// ReflectTest2.class.getResourceAsStream("config.properties");
// Collection collection = new ArrayList();
反射的基石:Class类
Class类代表Java类,它的各个实例对象又分别对应什么呢?
对应各个类在内存中的字节码,例如,Person类的字节码,ArrayList类的字节码,等等。
一个类被类加载器加载到内存中,占用一片存储空间,这个空间里面的内容就是类的字节码,不同的类的字节码是不同的,所以它们在内存中的内容是不同的,这一个个的空间可分别用一个个的对象来表示,这些对象显然具有相同的类型,这个类型是什么呢?
如何得到各个字节码对应的实例对象( Class类型)
类名.class,例如,System.class
对象.getClass(),例如,new Date().getClass()
九个预定义Class实例对象:
参看Class.isPrimitive方法的帮助
Int.class == Integer.TYPE
boolean |
isPrimitive() 判定指定的 Class 对象是否表示一个基本类型。 |
数组类型的Class实例对象
Class.isArray()
Class.isArray()
boolean |
isArray() 判定此 Class 对象是否表示一个数组类。 |
总之,只要是在源程序中出现的类型,都有各自的Class实例对象,例如,int[],void…
反射概念
反射就是把Java类中的各种成分映射成相应的java类。
表示java类的Class类显然要提供一系列的方法,来获得其中的变量,方法,构造方法,修饰符,包等信息,这些信息就是用相应类的实例对象来表示,它们是Field、Method、Contructor、Package等等。
Constructor类:代表某个类中的一个构造方法
Class类中的方法
Constructor<T> |
getConstructor(Class<?>... parameterTypes) 返回一个 Constructor 对象,它反映此 Class 对象所表示的类的指定公共构造方法。 |
Constructor<?>[] |
getConstructors() 返回一个包含某些 Constructor 对象的数组,这些对象反映此 Class 对象所表示的类的所有公共构造方法。 |
得到某个类所有的构造方法:
例子:Constructor [] constructors= Class.forName("java.lang.String").getConstructors();
得到某一个构造方法:
例子: Constructor constructor = Class.forName(“java.lang.String”).getConstructor(StringBuffer.class); //获得方法时要用到类型
例子:Constructor [] constructors= Class.forName("java.lang.String").getConstructors();
得到某一个构造方法:
例子: Constructor constructor = Class.forName(“java.lang.String”).getConstructor(StringBuffer.class); //获得方法时要用到类型
Constructor con = Class.forName("java.lang.String").getConstructor(
int.class );
java.lang.reflect.Constructor类中的方法
创建实例对象:
通常方式:String str = new String(new StringBuffer("abc"));
反射方式: String str = (String)constructor.newInstance(new StringBuffer("abc")); //调用获得的方法时要用到上面相同类型的实例对象
T |
newInstance(Object... initargs) 使用此 Constructor 对象表示的构造方法来创建该构造方法的声明类的新实例,并用指定的初始化参数初始化该实例。 |
String str = (String) con.newInstance( new StringBuffer("qwrt" ));
Class.newInstance()方法:
T |
newInstance() 创建此 Class 对象所表示的类的一个新实例。 |
例子:String obj = (String)Class.forName("java.lang.String").newInstance();
该方法内部先得到默认的构造方法,然后用该构造方法创建实例对象。
该方法内部的具体代码是怎样写的呢?用到了缓存机制来保存默认构造方法的实例对象。
该方法内部的具体代码是怎样写的呢?用到了缓存机制来保存默认构造方法的实例对象。
Field类:代表某个类中的一个成员变量
Class类中的方法
Field |
getField(String name) 返回一个 Field 对象,它反映此 Class 对象所表示的类或接口的指定[公共public]成员字段。 |
Field[] |
getFields() 返回一个包含某些 Field 对象的数组,这些对象反映此 Class 对象所表示的类或接口的所有可访问公共字段。 |
得到的Field对象是对应到类上面的成员变量,还是对应到对象上的成员变量?类只有一个,而该类的实例对象有多个,如果是与对象关联,哪关联的是哪个对象呢?所以字段fieldX 代表的是x的定义,而不是具体的x变量。
Field y = fi.getClass().getField(
"a"
);
"a"
);
java.lang.reflect.Field中的方法
System.
out
.println(fa.get(fi));
out
.println(fa.get(fi));
访问非公有成员
Field |
getDeclaredField(String name) 返回一个 Field 对象,该对象反映此 Class 对象所表示的类或接口的指定已声明字段。 |
Field[] |
getDeclaredFields() 返回 Field 对象的一个数组,这些对象反映此 Class 对象所表示的类或接口所声明的所有字段。 |
void |
setAccessible(boolean flag) 将此对象的 accessible 标志设置为指示的布尔值。 |
Fie fi =
new
Fie(2, 3);
new
Fie(2, 3);
Field fa = fi.getClass().getDeclaredField(
"a"
);
"a"
);
fa.setAccessible(
true
);
true
);
System.
out
.println(fa.get(fi));
out
.println(fa.get(fi));
高级用法:问题:将任意一个对象中的所有String类型的成员变量所对应的字符串内容中的"b"改成"a"。
public
class
Ts0 {
class
Ts0 {
public
static
void
main(String[] args)
throws
Exception {
Text text =
new
Text();
new
Text();
Field[] fields = text.getClass().getFields();
for
(Field field : fields) {
// if(field.getType().equals(String.class)){
if
(field.getType() == String.
class
) {
String oldVar = (String) field.get( text);
String newVar = oldVar.replace(
'a'
,
'1'
);
'a'
,
'1'
);
field.set( text, newVar);
}
}
System.
out
.println(text);
out
.println(text);
}
}
class
Text {
Text {
public
String
a
=
"jfaiweonv"
;
public
String
b
=
"fewiojavo"
;
public
String
c
=
"fejwaofkpwel"
;
@Override
public
String toString() {
return
a
+
":"
+
b
+
":"
+
c
;
}
}
Method类代表某个类中的一个成员方法
Class类中的方法
Method |
getMethod(String name, Class<?>... parameterTypes) 返回一个 Method 对象,它反映此 Class 对象所表示的类或接口的指定公共成员方法。 |
Method[] |
getMethods() 返回一个包含某些 Method 对象的数组,这些对象反映此 Class 对象所表示的类或接口(包括那些由该类或接口声明的以及从超类和超接口继承的那些的类或接口)的公共 member 方法。 |
Method method = Class. forName(
"java.lang.String"
).getMethod(
"substring"
,
int
.
class
,
int
.
class
);
"java.lang.String"
).getMethod(
"substring"
,
int
.
class
,
int
.
class
);
java.lang.reflect.Method类中的方法
Object |
invoke(Object obj, Object... args) 对带有指定参数的指定对象调用由此 Method 对象表示的底层方法。
|
-
如果底层方法是静态的,那么可以忽略指定的
obj
参数。该参数可以为 null。如果底层方法
所需的形参数为 0,则所提供的args
数组长度可以为 0 或 null。
Method method = Class.forName(
"java.lang.String"
).getMethod(
"substring"
,
int
.
class
,
int
.
class
);
"java.lang.String"
).getMethod(
"substring"
,
int
.
class
,
int
.
class
);
String a =(String) method.invoke(
new
String(
"abcdefg"
), 1,3);
new
String(
"abcdefg"
), 1,3);
System.
out
.println(a);
out
.println(a);
用反射方式执行某个类中的main方法
写一个程序,这个程序能够根据用户提供的类名,去执行该类中的main方法。用普通方式调完后,大家要明白为什么要用反射方式去调啊?
Class clazz = Class.forName(args[0]);
Method method = clazz.getMethod(
"main"
, String[].
class
);
"main"
, String[].
class
);
method.invoke(
null
, (Object)
new
String[] {
"123"
,
"456"
,
"789"
});
null
, (Object)
new
String[] {
"123"
,
"456"
,
"789"
});
数组的反射
具有相同维数和元素类型的数组属于同一个类型,即具有相同的Class实例对象(此处比较与值无关)。
如:int [][] a = new int [1][3]; int [][] b = new int [1][3]; a.getclass()==b.getclass
代表数组的Class实例对象的getSuperClass()方法返回的父类为Object类对应的Class。
基本类型的一维数组可以被当作Object类型使用,不能当作Object[]类型使用;非基本类型的一维数组,既可以当做Object类型使用,又可以当做Object[]类型使用。
Arrays.asList()方法处理int[]和String[]时的差异。
int
[] a = { 1, 2, 3, 4, 5 };
String[] b = {
"a"
,
"b"
,
"c"
,
"d"
,
"e"
};
"a"
,
"b"
,
"c"
,
"d"
,
"e"
};
System.
out
.println(Arrays.asList(a));
//[[I@34780af5]
out
.println(Arrays.asList(a));
//[[I@34780af5]
System.
out
.println(Arrays.asList(b));
//[a, b, c, d, e]
out
.println(Arrays.asList(b));
//[a, b, c, d, e]
java.lang.reflect.Array:工具类用于完成对数组的反射操作。
String[] b = {
"a"
,
"b"
,
"c"
,
"d"
,
"e"
};
"a"
,
"b"
,
"c"
,
"d"
,
"e"
};
if
(b.getClass().isArray()) {
System.
out
.println(Array.getLength( b));
out
.println(Array.getLength( b));
Array. set(b , 1,
"1"
);
"1"
);
System.
out
.println(Arrays.asList( b));
out
.println(Arrays.asList( b));
}
反射的作用:实现框架功能
什么是框架,例如,我们要写程序扫描.java文件中的注解,要解决哪些问题:读取每一样,在每一个中查找@,找到的@再去查询一个列表,如果@后的内容出现在了列表中,就说明这是一个我能处理和想处理的注解,否则,就说明它不是一个注解或者说至少不是一个我感兴趣和能处理的注解。接着就编写处理这个注解的相关代码。现在sun提供了一个apt框架,它会完成所有前期工作,只需要我们提供能够处理的注解列表,以及处理这些注解的代码。Apt框找到我们感兴趣的注解后通知或调用我们的处理代码去处理。
你做的门调用锁,锁是工具,你做的门被房子调用,房子是框架,房子和锁都是别人提供的。
你做的门调用锁,锁是工具,你做的门被房子调用,房子是框架,房子和锁都是别人提供的。
import
java.io.InputStream;
java.io.InputStream;
import
java.util.Collection;
java.util.Collection;
import
java.util.Properties;
java.util.Properties;
public
class
Ts {
class
Ts {
public
static
void
main(String[] args)
throws
Exception {
// 应该先直接用ArrayList和HashSet,然后才引入从配置文件读,这样便于学员学习。
Properties props =
new
Properties();
new
Properties();
// 先演示相对路径的问题
完整路径:Jsp中
/*getRealPath();//金山词霸/内部 !一定要记住用完整的路径,但完整的路径不是硬编码,而是运算出来的。*/
相对路径:
Class类也提供getResourceAsStream
ClassLoader |
getClassLoader() 返回该类的类加载器。 |
InputStream |
getResourceAsStream(String name) 查找具有给定名称的资源。 |
// InputStream ips = new FileInputStream("config.properties");
/*
* 一个类加载器能加载.class文件,那它当然也能加载 classpath环境下的其他文件,既然它有如此能力,它没有理由不顺带提供这样一个方法。
* 它也只能加载 classpath环境下的那些文件。注意:直接使用类加载器时,不能以/打头。
*/
// InputStream ips =
// ReflectTest2.class.getClassLoader().getResourceAsStream("cn/ itcast/javaenhance /config.properties");
IDE会把src中所有的非.java的文件自动搬到bin中,所以相对路径没问题,推荐使用
// Class提供了一个便利方法,用加载当前类的那个类加载器去加载相同包目录下的文件
// InputStream ips =
// ReflectTest2.class.getResourceAsStream("config.properties");
InputStream ips = Ts.
class
.getResourceAsStream(
"temp.properties"
);
class
.getResourceAsStream(
"temp.properties"
);
props.load(ips);
ips.close();
String className = props.getProperty(
"className"
);
"className"
);
Class clazz = Class.forName(className);
Collection collection = (Collection) clazz.newInstance();
// Collection collection = new ArrayList();
collection.add(1);
System.
out
.println(collection.size());
out
.println(collection.size());
}
}