----------android培训、java培训、java学习型技术博客、期待与您交流!------------
在此,分享一下自己学习JAVA的学习心得。有不对的地方请帮忙改正,也希望对想学java的同学有帮助!
Java高兴技术
--反射
反射技术:
反射在本质上就是把Java类中的各种成分映射成相应的java类。其实就是动态加载一个指定的类,并获取该类中的所有的内容。而且将字节码文件封装成对象,并将字节码文件中的内容都封装成对象,这样便于操作这些成员。
静态编译:在编译时确定类型,绑定对象,即通过。
动态编译:运行时确定类型,绑定对象。动态编译最大限度发挥了java的灵活性,体现了多 态的应用,有以降低类之间的藕合性。
反射的基本步骤:
1、获得Class对象,就是获取到指定的名称的字节码文件对象。
2、实例化对象,获得类的属性、方法或构造函数。
3、访问属性、调用方法、调用构造函数创建对象。
Class类是Reflection API 中的核心类,它有以下方法:
Java程序中的各个Java类属于同一类事物,描述这类事物的Java类名就是Class。
得到各个字节码对应的实例对象的方法:( Class类型):
1、类名.class。
例如,System.class
2、对象.getClass()。
例如,new Date().getClass()
3、Class.forName("类名")。
例如,Class.forName("java.util.Date");
知识点:.class和Class.forName的区别:
后者可以在不确定要获得字节码的具体类的时候使用,传入的是一个字符串,经常在框架中使用。
九个预定义类型的Class对象:
基本的 Java 类型(boolean
、byte
、char
、short
、int
、long
、float
和double
)和关键 字void
也表示为Class
对象。基本类型的字节码获取方式只有一种就是类名.class。
<span style="font-size:18px;">public class Demo {
public static void main(String[] args) throws ClassNotFoundException {
//
String str1 = "abc";
Class cls1 = str1.getClass();
Class cls2 = String.class;
Class cls3 = Class.forName("java.lang.String");
System.out.println(cls1 == cls2);//返回true
System.out.println(cls3 == cls2);//返回true
/*
* 有九种预定义的 Class 对象,表示八个基本类型和 void。
* 这些类对象由 Java 虚拟机创建,与其表示的基本类型同名,
* 即 boolean、byte、char、short、int、long、float
* 和 double。
* */
System.out.println(cls1.isPrimitive());//判定指定的 Class 对象是否表示一个基本类型。
System.out.println(int.class.isPrimitive());//true
System.out.println(int.class == Integer.class);//false
System.out.println(int.class == Integer.TYPE);//true
System.out.println(int[].class.isPrimitive());//false</span><span style="font-family: 宋体, 'Arial Narrow', arial, serif;"><span style="font-size:18px;">//判断是否表示一个基本类型</span></span><span style="font-size:18px;">
System.out.println(int[].class.isArray());//true//判断是否表示一个数组类型
}
}</span>
ReflectPoint pt1 = new ReflectPoint(3,5);
Field fieldY = pt1.getClass().getField("y");//获取公有的成员变量
System.out.println(fieldY.get(pt1));
Field fieldX = pt1.getClass().getDeclaredField("x");//强行获取私有的成员变量
fieldX.setAccessible(true);//可以获取私有成员变量的值
System.out.println(fieldX.get(pt1));
得到类中的某一个方法:
例子:
Method charAt = Class.forName("java.lang.String").getMethod("charAt", int.class);
调用方法:
通常方式:System.out.println(str.charAt(1));
反射方式: System.out.println(charAt.invoke(str, 1));
如果传递给Method对象的invoke()方法的第一个参数为null,说明该Method对象对应的是一个静态方法!
得到某个类所有的构造方法:
例子:
Constructor [] constructors= Class.forName("java.lang.String").getConstructors();
得到某一个构造方法:
例子:
Constructor constructor=Class.forName(“java.lang.String”).getConstructor(StringBuffer.class);
获得方法时要用到类型:
创建实例对象:
通常方式:String str = new String(new StringBuffer("abc"));
反射方式: String str = (String)constructor.newInstance(new StringBuffer("abc"));
调用获得的方法时要用到上面相同类型的实例对象
Class.newInstance()方法:
例子:String obj = (String)Class.forName("java.lang.String").newInstance();
该方法内部先得到默认的构造方法,然后用该构造方法创建实例对象。也就是使用String类的无参构造
方法创建了一个String对象。
以下是用参数为StringBuffer类型的构造函数创建一个String类型的实例对象:
数组与Object的关系及其反射类型
1. 具有相同维数和元素类型的数组属于同一个类型,即具有相同的Class实例对象。
<span style="font-size:18px;">public class ReflectTest {
public static void main(String[] args) throws Exception {
int [] a1 = new int[3];
int [] a2 = new int[4];
int [][] a3 = new int[2][3];
String[] a4 = new String[3];
System.out.println(a1.getClass() == a2.getClass());
//结果:true
System.out.println(a1.getClass() == a4.getClass());
//结果:false
System.out.println(a1.getClass() == a3.getClass());
//结果:false
}
}
</span>
2. 代表数组的class实例对象的getSuperClass()方法返回的父类为Object类对应的Class。
3. 基本类型的一维数组可以被当作Object类型使用,不能当作Object[]类型使用;非基本类型的一维数组,即可以做Object类型使用,又可以当作Object[]类型使用。
4. Array.asList()方法处理int[]和String[]时的差异
<span style="font-size:18px;">public class ReflectTest {
public static void main(String[] args) throws Exception {
int [] a5 = new int[]{1,2,3};
String[] a6 = new String[]{"a" ,"b" ,"c" };
//直接使用System.out.println无法打印出数组的内容
System. out .println(a5);
//结果:[I@18a992f
System. out .println(a6);
//结果:[Ljava.lang.String;@4f1d0d
//通过Arrays.asList方法打印出数组的内容
System. out .println(Arrays.asList(a5));
//结果:[[I@18a992f]
//原因是因为JDK1.4中为Arrays.asList(Object[] a),JDK1.5中为Arrays.asList(T... a)。
//a5是int[]类型,JDK1.4中的asList方法处理不了,JDK1.5可以处理。但是JDK1.5将 int数组整体作为一个参数进行处理。
//因此最终结果就是将 int[]进行了封装,结果类型也就成了[[I。
System. out .println(Arrays.asList(a6));
//结果:[a, b, c]
}
}</span>
5. Array工具类用于完成数组的反射操作。
<span style="font-size:18px;">public class ReflectTest {
public static void main(String[] args) throws Exception {
String[] a1 = new String[]{"a" ,"b" ,"c" };
String a2 = "xyz";
printObject(a1);
printObject(a2);
}
public static void printObject(Object obj){
Class clazz = obj.getClass();
if(clazz.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);
}
}
}</span>
6. ArrayList和HashSet的比较及Hashcode分析。
ArrayList:
<span style="font-size:18px;"> Collection collections = new ArrayList();
ReflectPoint pt1 = new ReflectPoint(3, 3);
ReflectPoint pt2 = new ReflectPoint(5, 5);
ReflectPoint pt3 = new ReflectPoint(3, 3);
collections.add(pt1);
collections.add(pt2);
collections.add(pt3);
collections.add(pt1);
System. out.println(collections.size());
//结果:4 Collection collections = new ArrayList();
ReflectPoint pt1 = new ReflectPoint(3, 3);
ReflectPoint pt2 = new ReflectPoint(5, 5);
ReflectPoint pt3 = new ReflectPoint(3, 3);
collections.add(pt1);
collections.add(pt2);
collections.add(pt3);
collections.add(pt1);
System. out.println(collections.size());
//结果:4</span>
HashSet:
Collection collections = new HashSet();
ReflectPoint pt1 = new ReflectPoint(3, 3);
ReflectPoint pt2 = new ReflectPoint(5, 5);
ReflectPoint pt3 = new ReflectPoint(3, 3);
collections.add(pt1);
collections.add(pt2);
collections.add(pt3);
collections.add(pt1);
System. out.println(collections.size());
//结果:3
分析:
当集合为HashSet时,需要通过比较hashcode值以及equals方法是否返回true决定是否放入。如果hashcode值相等并且equals方法返回true,那么就不会放入。因此,集合size为3。
hashCode()和equals()覆盖:
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + x;
result = prime * result + y;
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true ;
if (obj == null)
return false ;
if (getClass() != obj.getClass())
return false ;
ReflectPoint other = (ReflectPoint) obj;
if (x != other.x)
return false ;
if (y != other.y)
return false ;
return true ;
}
此时,运行ReflectTest.java结果为2。
注意:
当一个对象被存储进HashSet集合中以后,就不能修改这个对象中的那些参与计算哈希值的字段了,否则,对象修改后的哈希值与最初存储进HashSet集合中时的哈希值就不同了,在这种情况下,即使在contains方法使用该对象的当前引用作为的参数去HashSet集合中检索对象,也将返回找不到对象的结果,这也会导致无法从HashSet集合中单独删除当前对象,从而造成内存泄露。
private static void changeStringValue(Object obj) throws Exception {
Field[] fields = obj.getClass().getFields();//获取所有公有的成员变量
for(Field field : fields) {//遍历这些成员变量
if(field.getType() == String.class){//判断该变量的类型是不是String
String oldValue = (String)field.get(obj);
String newValue = oldValue.replace('b', 'a');//替换
field.set(obj, newValue);//设置新值
}
}
}
ReflectPoint类:
public class ReflectPoint {
private int x;
public int y;
public String str1 = "ball";
public String str2 = "basketball";
public String str3 = "itcast";
public ReflectPoint(int x, int y) {
super();
this.x = x;
this.y = y;
}
@Override
public String toString() {
return str1+":"+str2+":"+str3;
}
}
jdk1.4和jdk1.5的invoke方法的区别:
Jdk1.5:public Object invoke(Object obj,Object... args) 可变参数;
Jdk1.4:public Object invoke(Object obj,Object[] args),即按jdk1.4的语法,需要将一个数组作为参数
传递给invoke方法,数组中的每个元素分别对应被调用方法中的一个参数。
main方法的调用:
String startingClassName = args[0];
Method mainMethod = Class.forName(startingClassName).getMethod("main", String[].class);
mainMethod.invoke(null, (Object)new String[]{"111","222","333"});
为了兼容1.4版本没有可变参数方法,传入的一个数组对象会被自动拆包,变为多个参数,因此要打包成
一个Object对象传入,避免被拆包。
用类加载器的方式管理资源和配置文件:
方式一:采用类加载器进行加载,使用相对路径的方式
InputStream is = ReflectTest.class.getClassLoader().getResourceAsStream("com/mytest/config.properties");
方式二:利用Class方式进行加载,使用相对路径的方式
nputStream is = ReflectTest.class.getResourceAsStream("config.properties");
方式三:利用Class方式进行加载,使用绝对路径的方式
InputStream is = ReflectTest.class .getResourceAsStream("com/mttest/config.properties");