--------------------- android培训、java培训、java学习型技术博客、期待与您交流! -------------------
1 代理
生活中的代理:
|----武汉人从武汉的代理商手中买联想电脑和直接跑到北京传智播客旁边来找联想总部买电脑,你觉得最终的主体业务目标有什么区别吗?基本上一样吧,都解决了核心问题,但是,一点区别都没有吗?从代理商那里买真的一点好处都没有吗?
|----程序中的代理
|----要为已存在的多个具有相同接口的目标类的各个方法增加一些系统功能,例如,异常处理、日志、计算方法的运行时间、事务管理、等等,你准备如何做?
|----编写一个与目标类具有相同接口的代理类,代理类的每个方法调用目标类的相同方法,并在调用方法时加上系统功能的代码。 (参看下页的原理图)
|----如果采用工厂模式和配置文件的方式进行管理,则不需要修改客户端程序,在配置文件中配置是使用目标类、还是代理类,这样以后很容易切换,譬如,想要日志功能时就配置代理类,否则配置目标类,这样,增加系统功能很容易,以后运行一段时间后,又想去掉系统功能也很容易。
AOP:
系统中存在交叉业务,一个交叉业务就是要切入到系统中的一个方面,如下所示:
安全 事务 日志
StudentService ------|----------|------------|-------------
CourseService ------|----------|------------|-------------
MiscService ------|----------|------------|-------------
用具体的程序代码描述交叉业务:
method1 method2 method3
{ { {
------------------------------------------------------切面
.... .... ......
------------------------------------------------------切面
} } }
交叉业务的编程问题即为面向方面的编程(Aspect oriented program ,简称AOP),AOP的目标就是要使交叉业务模块化。可以采用将切面代码移动到原始方法的周围,这与直接在方法中编写切面代码的运行效果是一样的,如下所示:
------------------------------------------------------切面
func1 func2 func3
{ { {
.... .... ......
} } }
------------------------------------------------------切面
使用代理技术正好可以解决这种问题,代理是实现AOP功能的核心和关键技术。
动态代理技术
要为系统中的各种接口的类增加代理功能,那将需要太多的代理类,全部采用静态代理方式,将是一件非常麻烦的事情!写成百上千个代理类,是不是太累!
|----JVM可以在运行期动态生成出类的字节码,这种动态生成的类往往被用作代理类,即动态代理类。
l----JVM生成的动态类必须实现一个或多个接口,所以,JVM生成的动态类只能用作具有相同接口的目标类的代理。
l----CGLIB库可以动态生成一个类的子类,一个类的子类也可以用作该类的代理,所以,如果要为一个没有实现接口的类生成动态代理类,那么可以使用CGLIB库。
l----代理类的各个方法中通常除了要调用目标的相应方法和对外返回目标返回的结果外,还可以在代理方法中的如下四个位置加上系统功能代码:
1.在调用目标方法之前
2.在调用目标方法之后
3.在调用目标方法前后
4.在处理目标方法异常的catch块中(应用范围比较广)
分析JVM动态生成的类
|---创建实现了Collection接口的动态类和查看其名称,分析Proxy.getProxyClass方法的各个参数。
|---编码列出动态类中的所有构造方法和参数签名
|---编码列出动态类中的所有方法和参数签名
|---创建动态类的实例对象
用反射获得构造方法
编写一个最简单的InvocationHandler类
调用构造方法创建动态类的实例对象,并将编写的InvocationHandler类的实例对象传进去
打印创建的对象和调用对象的没有返回值的方法和getClass方法,演示调用其他有返回值的方法报告了异常。
将创建动态类的实例对象的代理改成匿名内部类的形式编写,锻炼大家习惯匿名内部类。
总结思考:让jvm创建动态类及其实例对象,需要给它提供哪些信息?
三个方面:
• 生成的类中有哪些方法,通过让其实现哪些接口的方式进行告知;
• 产生的类字节码必须有个一个关联的类加载器对象;
• 生成的类中的方法的代码是怎样的,也得由我们提供。把我们的代码写在一个约定好了接口对象的方法中,把对象传给它,它调用我的方法,即相当于插入了我的代码。提供执行代码的对象就是那个InvocationHandler对象,它是在创建动态类的实例对象的构造方法时传递进去的。在上面的InvocationHandler对象的invoke方法中加一点代码,就可以看到这些代码被调用运行了。
用Proxy.newInstance方法直接一步就创建出代理对象。
让动态生成的类成为目标类的代理
怎样将目标类传进去?
|----直接在InvocationHandler实现类中创建目标类的实例对象,可以看运行效果和加入日志代码,但没有实际意义。
|----为InvocationHandler实现类注入目标类的实例对象,不能采用匿名内部类的形式了。
|----让匿名的InvocationHandler实现类访问外面方法中的目标类实例对象的final类型的引用变量。
将创建代理的过程改为一种更优雅的方式,eclipse重构出一个getProxy方法绑定接收目标同时返回代理对象,让调用者更懒惰,更方便,调用者甚至不用接触任何代理的API。
将系统功能代码模块化,即将切面代码也改为通过参数形式提供,怎样把要执行的系统功能代码以参数形式提供?
|-----把要执行的代码装到一个对象的某个方法里,然后把这个对象作为参数传递,接收者只要调用这个对象的方法,即等于执行了外界提供的代码!
|-----为bind方法增加一个Advice参数。
2 反射
java反射作用的简介:
java 反射是一种强大的工具,它是我们可以在运行时装载代码,而无需在对象之间进行源代码的链接,从而是代码更灵活。在编译时,java编译程序保证了私有成员的私有特性,从而一个私有方法和私有变量不能被其他静态类引用。然而,通过java反射机制使得我们可以在运行时查询以及访问变量和方法。由于反射是动态的,因此编译时的检查就不再起作用了。也就是说反射可以绕过访问权限,访问到非公有方法和成员。可能这一点会引起安全问题的讨论。反射的使用帮助解决很多复杂的问题,其运行时的类型检查,动态调用,代理的实现等。反射为我们写程序带来了很大的灵活性,很多功能都是基于反射。
java反射的操作:
java反射可以大致理解反射就是将java中的各个成分提取出来并包装成单独功能的类。通过 Class 类获取成员变量、成员方法、接口、超类、构造方法等
简单举例如下:
Class 类:代表一个类。
通过 getClasses()将类的成员变量包装成Class类。
Field 类:代表类的成员变量(成员变量也称为类的属性)。
通过getFields()将类的成员变量包装成Field类。
Method 类:代表类的方法。
通过getMethods()将类的方法包装成 Method类。
Constructor 类:代表类的构造方法。
通过getConstructors()将类的成员变量包装成 Constructor类。
获得Class的方法:
1)运用 对象.getClass()
注:每个class 都有此函数
String str = "abc";
Class c1 = str.getClass();
2)运用 类名.class
Button b = new Button();
Class c1 = b.getClass();
3)运用 Class.forName()(最常被使用,通常情况下是之前在不知道类的情况使用)
Class c1 = Class.forName ("java.lang.String");
Class c2 = Class.forName ("java.awt.Button");
Class c5 = Class.forName (".class");
代码举例:
1. 得到某个对象的属性(所以字段fieldX 代表的是x的定义,而不是具体的每个类的x变量)
public Object getProperty(Object owner, String fieldName) throws Exception
{
Class ownerClass = owner.getClass();
Field field = ownerClass.getField(fieldName);
Object property = field.get(owner);
return property;
}
Class ownerClass = owner.getClass():得到该对象的Class。
Field field = ownerClass.getField(fieldName):通过Class得到类声明的属性。
Object property = field.get(owner):通过对象得到该属性的实例,如果这个属性是非公有的,这里会报IllegalAccessException。
2. 得到某个类的静态属性
public Object getStaticProperty(String className, String fieldName)
throws Exception {
Class ownerClass = Class.forName(className);
Field field = ownerClass.getField(fieldName);
Object property = field.get(ownerClass);
return property;
}
Class ownerClass = Class.forName(className) :首先得到这个类的Class。
Field field = ownerClass.getField(fieldName):和上面一样,通过Class得到类声明的属性。
Object property = field.get(ownerClass) :这里和上面有些不同,因为该属性是静态的,所以直接从类的Class里取。
3. 执行某对象的方法
public Object invokeMethod(Object owner, String methodName, Object[] args) throws Exception
{
Class ownerClass = owner.getClass();
Class[] argsClass = new Class[args.length];
for (int i = 0, j = args.length; i < j; i++)
{
argsClass[i] = args[i].getClass();
}
Method method = ownerClass.getMethod(methodName, argsClass);
return method.invoke(owner, args);
}
Class owner_class = owner.getClass() :第一步,还是必须得到这个对象的Class。
Class[] argsClass = new Class[args.length];第二步,配置参数的Class数组,作为寻找Method的条件。
Method method = ownerClass.getMethod(methodName, argsClass):通过Method名和参数的Class数组得到要执行的Method。
method.invoke(owner, args):执行该Method,invoke方法的参数是执行这个方法的对象,和参数数组。返回值是Object,也既
是该方法的返回值。
4. 执行某个类的静态方法
public Object invokeStaticMethod(String className, String methodName,
Object[] args) throws Exception {
Class ownerClass = Class.forName(className);
Class[] argsClass = new Class[args.length];
for (int i = 0, j = args.length; i < j; i++) {
argsClass[i] = args[i].getClass();
}
Method method = ownerClass.getMethod(methodName, argsClass);
return method.invoke(null, args);
}
注意:
如果传递给Method对象的invoke()方法的第一个参数为null,说明该Method对象对应的是一个静态方法
基本的原理和实例3相同,不同点是最后一行,invoke的一个参数是null,因为这是静态方法,不需要借助实例运行。
5. 新建实例
public Object newInstance(String className, Object[] args) throws Exception
{
Class newoneClass = Class.forName(className);
Class[] argsClass = new Class[args.length];
for (int i = 0, j = args.length; i < j; i++)
{
argsClass[i] = args[i].getClass();
}
Constructor cons = newoneClass.getConstructor(argsClass);
return cons.newInstance(args);
}
这里说的方法是执行带参数的构造函数来新建实例的方法。如果不需要参数,可以直接使用newoneClass.newInstance()来实现。
Class newoneClass = Class.forName(className):
第一步,得到要构造的实例的Class。
第二步,得到参数的Class数组。
Constructor cons = newoneClass.getConstructor(argsClass):得到构造子。
cons.newInstance(args):新建实例。
6. 判断是否为某个类的实例
public boolean isInstance(Object obj, Class cls) {
return cls.isInstance(obj);
}
7. 得到数组中的某个元素
public Object getByArray(Object array, int index) {
return Array.get(array,index);
注意:
具有相同维数和元素类型的数组属于同一个类型,即具有相同的Class实例对象。
8 . 执行某个类的main方法
Class clazz = Class.forName(arg[0]);
Method mMain = clazz.getMethod("main", String[].class);
mMain.invoke(null,new Object[]{new String[]{"aaa","bbb"}});
mMain.invoke(null,(Object)new String[]{"aaa","bbb"});
注意:
会按jdk1.4的语法进行处理,即把数组打散成为若干个单独的参数。所以,在给main方
法传递参数时,不能使用代码mainMethod.invoke(null,new String[]{“xxx”})
而是使用 mMain.invoke(null,new Object[]{new String[]{"aaa","bbb"}});
或者 mMain.invoke(null,(Object)new String[]{"aaa","bbb"});
3 数组反射
问题:
1.启动Java程序的main方法的参数是一个字符串数组,即public static void main(String[] args),
通过反射方式来调用这个main方法时,如何为invoke方法传递参数呢?
2.按jdk1.5的语法,整个数组是一个参数,而按jdk1.4的语法,数组中的每个元素对应一个参数,
当把一个字符串数组作为参数传递给invoke方法时,javac会到底按照哪种语法进行处理呢?
jdk1.5肯定要兼容jdk1.4的语法,会按jdk1.4的语法进行处理,即把数组打散成为若干个单独的参数。
所以,在给main方法传递参数时,不能使用代码mainMethod.invoke(null,new String[]{“xxx”}),javac
只把它当作jdk1.4的语法进行理解,而不把它当作jdk1.5的语法解释,因此会出现参数类型不对的问题。
解决办法:
mainMethod.invoke(null,new Object[]{new String[]{"xxx"}});
mainMethod.invoke(null,(Object)new String[]{"xxx"}); ,编译器会作特殊处理,编译时不
把参数当作数组看待,也就不会数组打散成若干个参数了
Object[] 与String[]没有父子关系,Object与String有父子关系,所以new Object[]{“aaa”,”bb”}
不能强制转换成new String[]{“aaa”,”bb”};,Object x = “abc”能强制转换成String x = “abc”。
main.invoke(null, (Object)(new Object[]{“aaa”,“xxx”}));不能调用public static void main(String [] args)
详细分析:
1.验证不同维数的数组是否是相同的Class对象
int [] a1 = new int [8];
int a2 = new int [9];
int [][] a3 = new int [6][4];
System.out.println(a3.getClass().getName());
System.out.println(a1.getClass() == a2.getClass()); //相等,具有相同的Class实例对象
System.out.println(a1.getClass() == a3.getClass()); //不相等,具有不相同的Class实例对象
System.out.println(a1.getClass().getName());// 打印结果为:[ I 标示整型数组
2. 验证不同维数的数组父类是否是相同
System.out.println(a1.getClass().getSuperclass().getName());// 打印结果为:java.lang.Object.
System.out.println(a3.getClass().getSuperclass().getName());// 打印结果为:java.lang.Object.
3. 验证不同维数的数组与Object数组和Object的关系
int[] a = new int[3];
Object obj = a;//正确!基本类型的数组能转换成 Object
//Object[] obj1 = a //有错!基本类型的数组不能转换成 Object 数组
// Object[] obj3 = a1//有错!基本类型的数组不能转换成 Object 数组
Object obj4 = a3;//正确!基本类型的二维数组能转换成 Object 数组
4. 在这里分析研究Arrays.asList()方法处理int[]和String[]时的差异
int [] a1 = new int []{1,2,3};
String[] a2 = new String[]{"A","B","C"};
System.out.println(Arrays.asList(a1));// 打印结果为:[[I@1cfb549].
说明int数组可以转换成List集合,并装入,一个int数组对象
System.out.println(Arrays.asList(a1));// 打印结果为:[A,B,C]
说明String数组可以转换成List集合,并装入,A,B,C对象
数组的反射应用:
private static void printObject(Object obj) {
Class clazz =obj.getClass();
if(obj.getClass().isArray()){
int len = Array.getLength(obj);
for(int i=0;i<len;i++) {
System.out.println(Array.get(obj, i));
}
} else {
System.out.println(obj);
}
}
需要取出每个元素对象,然后再对各个对象进行判断,因为其中每个具体元素的类型都可以不同,
则 :printObject(new String[]{"A","B","C"});//打印结果是 ABC
printObject("xyz"});//打印结果是 xyz
4 枚举
DK1.5引入了新的类型——枚举。在Java中它虽然算个“小”功能,却给我的开发带来了“大”方便。Jav对于枚举这说一说其常见的用法,枚举常见的有7正用法,一一举例说明。
枚举用法一:常量
在JDK1.5之前,我们定义常量都是:public static fianl....。现在好了,有了枚举,可以把相关的常量分组到一个枚举类型里,而且枚举提供了比常量更多的方法。
例子:
Java代码
1 public enum Color {
2 RED, GREEN, BLANK, YELLOW
3 }
枚举用法二:switch
JDK1.6之前的switch语句只支持int,char,enum类型,使用枚举,能让我们的代码可读性更强。
例子:
Java代码
4 enum Signal {
5 GREEN, YELLOW, RED
6 }
7 public class TrafficLight {
8 Signal color = Signal.RED;
9 public void change() {
10 switch (color) {
11 case RED:
12 color = Signal.GREEN;
13 break;
14 case YELLOW:
15 color = Signal.RED;
16 break;
17 case GREEN:
18 color = Signal.YELLOW;
19 break;
20 }
21 }
22 }
枚举用法三:向枚举中添加新方法
如果打算自定义自己的方法,那么必须在enum实例序列的最后添加一个分号。而且Java要求必须先定义enum实例。
例子:
Java代码
23 public enum Color {
24 RED("红色", 1), GREEN("绿色", 2), BLANK("白色", 3), YELLO("黄色", 4);
25 // 成员变量
26 private String name;
27 private int index;
28 // 构造方法
29 private Color(String name, int index) {
30 this.name = name;
31 this.index = index;
32 }
33 // 普通方法
34 public static String getName(int index) {
35 for (Color c : Color.values()) {
36 if (c.getIndex() == index) {
37 return c.name;
38 }
39 }
40 return null;
41 }
42 // get set 方法
43 public String getName() {
44 return name;
45 }
46 public void setName(String name) {
47 this.name = name;
48 }
49 public int getIndex() {
50 return index;
51 }
52 public void setIndex(int index) {
53 this.index = index;
54 }
55 }
枚举用法四:覆盖枚举的方法
下面给出一个toString()方法覆盖的例子。
例子:
Java代码
56 public enum Color {
57 RED("红色", 1), GREEN("绿色", 2), BLANK("白色", 3), YELLO("黄色", 4);
58 // 成员变量
59 private String name;
60 private int index;
61 // 构造方法
62 private Color(String name, int index) {
63 this.name = name;
64 this.index = index;
65 }
66 //覆盖方法
67 @Override
68 public String toString() {
69 return this.index+"_"+this.name;
70 }
71 }
枚举用法五:实现接口
所有的枚举都继承自java.lang.Enum类。由于Java不支持多继承,所以枚举对象不能再继承其他类。
例子:
Java代码
72 public interface Behaviour {
73 void print();
74 String getInfo();
75 }
76 public enum Color implements Behaviour{
77 RED("红色", 1), GREEN("绿色", 2), BLANK("白色", 3), YELLO("黄色", 4);
78 // 成员变量
79 private String name;
80 private int index;
81 // 构造方法
82 private Color(String name, int index) {
83 this.name = name;
84 this.index = index;
85 }
86 //接口方法
87 @Override
88 public String getInfo() {
89 return this.name;
90 }
91 //接口方法
92 @Override
93 public void print() {
94 System.out.println(this.index+":"+this.name);
95 }
96 }
枚举用法六:使用接口组织枚举
例子:
Java代码
97 public interface Food {
98 enum Coffee implements Food{
99 BLACK_COFFEE,DECAF_COFFEE,LATTE,CAPPUCCINO
100 }
101 enum Dessert implements Food{
102 FRUIT, CAKE, GELATO
103 }
104 }
枚举用法七:关于枚举集合的使用
java.util.EnumSet和java.util.EnumMap是两个枚举集合。EnumSet保证集合中的元素不重复;EnumMap中的key是enum类型,而value则可以是任意类型。关于这个两个集合的使用就不在这里赘述,可以参考JDK文档。
关于枚举的实现细节和原理请参考:
参考资料:《ThinkingInJava》第四版
例子:
105 public class TestEnum {
106 /*最普通的枚举*/
107 public enum ColorSelect {
108 red, green, yellow, blue;
109 }
110
111 /* 枚举也可以象一般的类一样添加方法和属性,你可以为它添加静态和非静态的属性或方法,这一切都象你在一般的类中做的那样. */
112 public enum Season {
113 // 枚举列表必须写在最前面,否则编译出错
114 winter, spring, summer, fall;
115
116 private final static String location = "Phoenix";
117
118 public static Season getBest() {
119 if (location.equals("Phoenix"))
120 return winter;
121 else
122 return summer;
123 }
124 }
125 /*还可以有构造方法*/
126 public enum Temp {
127 /*通过括号赋值,而且必须有带参构造器和一属性跟方法,否则编译出错
128 * 赋值必须是都赋值或都不赋值,不能一部分赋值一部分不赋值
129 * 如果不赋值则不能写构造器,赋值编译也出错*/
130 absoluteZero(-459), freezing(32),boiling(212), paperBurns(451);
131
132 private final int value;
133 public int getValue() {
134 return value;
135 }
136 //构造器默认也只能是private, 从而保证构造函数只能在内部使用
137 Temp(int value) {
138 this.value = value;
139 }
140 }
141
142 public static void main(String[] args) {
143 /*
144 * 枚举类型是一种类型,用于定义变量,以限制变量的赋值 赋值时通过"枚举名.值"来取得相关枚举中的值
145 */
146 ColorSelect m = ColorSelect.blue;
147 switch (m) {
148 /*注意:枚举重写了ToString(),说以枚举变量的值是不带前缀的
149 *所以为blue而非ColorSelect.blue
150 */
151 case red:
152 System.out.println("color is red");
153 break;
154 case green:
155 System.out.println("color is green");
156 break;
157 case yellow:
158 System.out.println("color is yellow");
159 break;
160 case blue:
161 System.out.println("color is blue");
162 break;
163 }
164 System.out.println("遍历ColorSelect中的值");
165 /*通过values()获得枚举值的数组*/
166 for (ColorSelect c : ColorSelect.values()) {
167 System.out.println(c);
168 }
169 System.out.println("枚举ColorSelect中的值有:"+ColorSelect.values().length+"个");
170 /*ordinal()返回枚举值在枚举中的索引位置,从0开始*/
171 System.out.println(ColorSelect.red.ordinal());//0
172 System.out.println(ColorSelect.green.ordinal());//1
173 System.out.println(ColorSelect.yellow.ordinal());//2
174 System.out.println(ColorSelect.blue.ordinal());//3
175
176 /*枚举默认实现了java.lang.Comparable接口*/
177 System.out.println(ColorSelect.red.compareTo(ColorSelect.green));
178
179 System.out.println(Season.getBest());
180
181 for(Temp t:Temp.values()){
182 /*通过getValue()取得相关枚举的值*/
183 System.out.println(t+"的值是"+t.getValue());
184 }
185
186 }
187 }
构造int型枚举:
在Java 1.5之前,我们经常会把类型、状态、性别等取值范围很小而且比较固定的数据存储为int型常量,在枚举类型出现之前,大家都不会觉得有什么不便,然而在枚举类型出现之后,这种方式的缺点就明显了,一个是不可罗列,另一个是弱类型。然而int型仍然有int型的好处,比如持久化存储、位运算都要比枚举类型更加方便,所以如果有一种方式能够把两者综合一下就好了,下面我就介绍一种把两者结合起来的小技巧。
“int型枚举”是为了简化概念取的一个名字,并不是很准确,姑且这样一叫吧,觉得名字不好并且恰好手里有砖的可以拍了。
首先我们声明一个接口,以方便标识“int型枚举”这个抽象概念,接口里定义一个toInt()方法,用以将枚举对象转化成int值:
[java] view plaincopy
188 public interface IntEnum {
189 int toInt();
190 }
现在我们用Sex类为例来实现这个IntEnum接口:
[java] view plaincopy
191 public enum Sex implements IntEnum {
192 MALE {
193 @Override
194 public int toInt()
195 {
196 return 1;
197 }
198 },
199 FEMALE {
200 @Override
201 public int toInt()
202 {
203 return 2;
204 }
205 }
206 }
从代码里我们可以看出,MALE转化成int是1,FEMALE是2(当然有人说MALE为1,FEMALE为0更形象,我想说,你太猥琐了)。
下面要做的是实现是从int到Sex的逆向转化,有经验的同学已经想到了,对,没错,我们只需要构造一个int到Sex的Map映射问题就解决了,为了让代码尽可能的复用,我们把这个方法写在一个工具类里,JDK针对特定类型的工具类都喜欢以[针对的类型s]的方式命名,所以我们也随大流,就叫IntEnums(如果你不知道泛型相关的知识,请自行翻书):
[java] view plaincopy
207 public class IntEnums {
208 public static <T extends Enum<T> & IntEnum> Map<Integer, T> map(Class<T> clazz)
209 {
210 Map<Integer, T> instanceMap = new HashMap<Integer, T>();
211 EnumSet<T> values = EnumSet.allOf(clazz);
212 for(T value : values)
213 {
214 instanceMap.put(value.toInt(), value);
215 }
216 return instanceMap;
217 }
218 }
现在工具类建好了,该考虑转化操作怎么搞才方便用的问题了,我们知道Enum都有一个valueOf方法,用于把字符串直接转化成Enum对象,我们可以借用这个方法名,重载它,实现IntEnum从int到枚举的转化,所以Sex的最终代码如下:
[java] view plaincopy
219 public enum Sex implements IntEnum {
220 MALE {
221 @Override
222 public int toInt()
223 {
224 return 1;
225 }
226 },
227 FEMALE {
228 @Override
229 public int toInt()
230 {
231 return 2;
232 }
233 };
234 private static Map<Integer, Sex> instanceMap = IntEnums.map(Sex.class);
235 public static Sex valueOf(int i)
236 {
237 return instanceMap.get(i);
238 }
239 }
当然,有的同学可能想到,把转化过程完全封装在IntEnums里不是更好,这种想法是好的,然而一并考虑到并发安全、效率等问题,可能变得有些复杂。
总结:
1.Class的实例是是一份字节码,一个类在虚拟机中通常只有一份字节码
2.反射就是把Java类中的各种成分映射成相应的java类
3.反射就是把Java类中的各种成分映射成相应的java类。
得到这些实 例对象通过反射可以使java程序在运行时判断任意一个对象所属的类。在运行时构造任意一个类的对象。在运行时判断任意一个类所具有的成员变量和方法。在运行时调用任意一个对象的方法等。
4.具有相同维数和元素类型的数组属于同一个类型,即具有相同的Class实例对象。
5.代表数组的Class实例对象的getSuperClass()方法返回的父类为Object类对应的Class。
6.基本类型的一维数组可以被当作Object类型使用,不能当作Object[]类型使用;非基本类型的一维数组,既可以当做Object类型使用,又可以当做Object[]类型使用。