转--黑马程序员-Java学习笔记(反射)
- 静态导入
package com.itheima.day1;
import static java.lang.Math.*;//导入Math类中的所有静态方法
public class StaticImport {
public static void main(String[] args)
{
int y = Math.abs(-6); //导入前需要添加Math类
int z = abs(-9); //导入后可以直接调用静态方法
System.out.println(z);
System.out.println(y);
}
}
总结:静态导入只是在原来导入的基础上多加了一个static的修饰符,这样就可以直接导入该类中所有的静态方法
格式:import static java.lang.Math.*;
2.可变参数与增强for循环
package com.itheima.day1;
public class VariableParameter {
public static void main(String[] args)
{
int x = add(1,3,5,6);
int y = add(2,4,5);
System.out.println(x);
System.out.println(y);
}
public static int add(int x,int...args)//可变参数
{
int sum = x;
//普通for循环
/*
for(int i =0;i<args.length;i++)
{
sum = (sum+args[i]);
}
*/
//增强的for循环
for(int arg : args) //增强的for循环
{
sum+=arg;
}
return sum;
}
}
总结:
可变参数,就是一个方法可以有多个同类型的参数的缩写方式;
格式:func(int...args)//int可以换成各种其他类型,args
增强的for循环,可以将for循环简写
格式:for(int arg:args)//循环遍历args的每一个参数
3.自动装箱与拆箱&享元模式
package com.itheima.day1;
public class AutoBox {
public static void main(String[] args)
{
Integer iObj = 3;//自动装箱,将3封装为Integer对象
System.out.println(iObj+12); //自动拆箱
//享元模式 flyweight Pattern
//采用一个共享来避免大量拥有相同内容对象的开销
//经典范例:String对象
Integer i1 = 126;
Integer i2 = 126;
System.out.println(i1 == i2); //结果为true,说明其指向同一个对象
Integer i3 = 129;
Integer i4 = 129;
System.out.println(i3 == i4); //结果为false,超过128,享元模式失效
}
}
总结:
自动装箱:将一个基本数据类型自动包装为对应的类对象
Integer x = 3;//将一个int类型数据3直接自动装箱为一个Integer对象x
自动拆箱:将一个基本数据包装类对象自动拆解为基本数据类型
System.out.println(x+12); //直接将Integer对象x自动拆箱为一个int类型数据
享元模式:
对于那些经常使用到的对象,建立专门的一块区域存储,后续再建立该对象,直接引用其地址即可,不需要重新创建对象,可以节省内存开销.
具体实例:
String x = "hello";
String y = "hello";
//x与y的值相等,都指向hello所在的内存区域
4.枚举类
用抽象类模拟枚举类
package com.itheima.day1;
public class EnumTest {
public static void main(String[] args)
{
WeekDay weekDay = WeekDay.MON;
System.out.println(weekDay.nextDay());
}
}
abstract class WeekDay //普通的Java类定义枚举
{
private WeekDay(){}
/* public WeekDay nextDay() //if else方法
{
if(this == SUN)
return MON;
else
return SUN;
}
*/
//采用抽象方法nextDay()将大量的if else语句转化为独立的方法
//因为WeekDay是一个抽象方法,因此需要用匿名内部类建立其实例对象
//每一个都是WeekDay的子类
public final static WeekDay SUN = new WeekDay(){
public WeekDay nextDay()
{
return MON;
}
};
public final static WeekDay MON = new WeekDay(){
public WeekDay nextDay()
{
return SUN;
}
};
public abstract WeekDay nextDay();
public String toString()
{
return this==SUN?"Sunday":"Monday";
}
}
枚举的基本应用:
package com.itheima.day1;
public class EnumTest {
public static void main(String[] args)
{
WeekDay1 weekDay = WeekDay1.MON;
System.out.println(weekDay.nextDay());
WeekDay weekDay2 =WeekDay.FRI;
System.out.println(weekDay2);
System.out.println(weekDay2.name()); //返回对象名称
System.out.println(weekDay2.ordinal()); //返回在enum类中的序号
System.out.println(WeekDay.valueOf("SUN").toString()); //
System.out.println(WeekDay.values().length); //返回enum类中的对象个数
}
public enum WeekDay{ //内部枚举类
SUN(2),MON(),TUE,WED,THU,FRI,SAT; //必须放在最前
private WeekDay(){System.out.println("First!!!");}; //必须是private
private WeekDay(int day){System.out.println("2nd!!!");};
}
//枚举只有一个成员时,可以作为一种单例的实现方式
public enum TrafficLamp{
RED(30){//调用父类带参数的构造方法
//每个变量都是父类的一个子类,因此需要实现父类的抽象方法
public TrafficLamp nextLamp()
{
return GREEN;
}
},
GREEN(45){
public TrafficLamp nextLamp()
{
return YELLOW;
}
},
YELLOW(5){
public TrafficLamp nextLamp()
{
return RED;
}
};
public abstract TrafficLamp nextLamp();
private int time;
private TrafficLamp(int time)
{
this.time = time;
}
}
}
5.反射
得到字节码的方式:
- Class cls1 = Date.class;//得到Date类的字节码
- new Date().getClass() //
- 3.Class.forName("java.lang.String")//返回String类的字节码
九个预定义class实例对象:
八个基本数据类型+void
Constructor类代表某个类中的一个构造方法
package com.itheima.day1;
import java.lang.reflect.Constructor;
public class ReflectTest {
public static void main(String[] args) throws Exception
{
String str1 = "abs";
Class cls1 = str1.getClass();
Class cls2 = String.class;
Class cls3 = Class.forName("java.lang.String");
System.out.println(cls1 == cls2); //true//所有String对象的字节码是一致的
System.out.println(cls1 == cls3); //true//三种方法获得的字节码都是一致的
System.out.println(cls1.isPrimitive()); //false//String不是基本数据类型
System.out.println(int.class.isPrimitive()); //true
System.out.println(int.class == Integer.class); //false//Integer字节码跟int不一致
System.out.println(int.class == Integer.TYPE); //true
System.out.println(int[].class.isPrimitive()); //false//数组不是基本数据类型
System.out.println(int[].class.isArray()); //true
//new String(new StringBuffer("abc"))
//编译时不看代码的执行,只是翻译成机器代码
//获得构造方法
Constructor<String> cons = String.class.getConstructor(StringBuffer.class);
//错误,不能这样传递,因为cons获得的构造方法必须带StringBuffer的参数
//String str = cons.newInstance("abc");
//创建实例对象
String str = cons.newInstance(new StringBuffer("abc"));
System.out.println(str);
}
}
//总之,只要是在源程序中出现的类型,都有各自的Class实例对象,例如int[],void
/*
* class也有newInstance()方法
class ->constructor ->new object
简化后:
class ->new object
*/
//反射会导致程序性能严重下降
Field:反射类的成员变量,可以通过getField()方法获得
{
ReflectPoint pt1 = new ReflectPoint(3,5);
Field fieldy = pt1.getClass().getField("y");//只能得到可见的参数
//filedy不是对象上的变量,而是类上的变量,因此用filedy.get(对象名)来获取其值;
System.out.println(fieldy.get(pt1));//得到具体实例对象中的成员变量值
Field fieldx = pt1.getClass().getDeclaredField("x");//可以看到private修饰的参数值
fieldx.setAccessible(true); //暴力获取private修饰的值
System.out.println(fieldx.get(pt1));
changeStringValue(pt1);
System.out.println(pt1);
}
private static void changeStringValue(Object obj)throws Exception
{
Field[] fields = obj.getClass().getFields();//得到对象的所有可见成员变量
for(Field field:fields)//增强的for循环,遍历所有成员变量
{
//if(field.getType().equals(String.class))
if(field.getType() == String.class)//因为是字节码,因此直接用等号判断
{
//得到String成员变量的值
String oldValue = (String)field.get(obj);
//将String成员变量中的所有出现的b字母替换成a字母
String newValue = oldValue.replace('b','a');
//将源对象中的该成员变量值替换
field.set(obj, newValue);
}
}
}
原字符串:

替换后的结果:

Method:反射类的方法,可以用getMethod方法获得
{
String str1 ="xyz";
//getMethod():第一个参数为想要获取的方法名,第二个参数为方法接受的参数类型
Method methodAt = String.class.getMethod("charAt", int.class);
System.out.println(methodAt.invoke(str1,1));
//用反射方式调用类的main方法
//当要启动的类名不确定,需要临时传递时,可以使用这种方式
String startClassName = args[0];
Method mainMethod = Class.forName(startClassName).getMethod("main", String[].class);
//mainMethod.invoke(null,new String[]{"111","222","333"});
//会报错,因为将参数拆成了3个
mainMethod.invoke(null,(Object)new String[]{"111","222","333"});
//告诉编译器我传递的是一个对象,不要将其拆包
//因为main方法是一个静态方法,因此不需要实例对象就可以调用
}
class TestArguments{
public static void main(String[] args)
{
for(String arg : args)
System.out.println(arg);
}
}
运行结果:

数组的反射:
{
int[] a1 = new int[]{1,5,2};
int[] a2 = new int[4];
int[][] a3 = new int[2][3];
String[] a4 = new String[]{"b","x","a"};
//由打印结果可知,两者字节码一致
System.out.println(a1.getClass() == a2.getClass());
//a1跟a3类型不同, 不能比较
//System.out.println(a1.getClass() == a3.getClass());
//a1跟a4类型不同, 不能比较
//System.out.println(a1.getClass() == a4.getClass());
//由打印结果可知,a1的字节码为[I,代表其为int类型的数组
System.out.println(a1.getClass().getName());
//父类的字节码为Object
System.out.println(a1.getClass().getSuperclass().getName());
System.out.println(a3.getClass().getName());
//String类的父类字节码为Object
System.out.println(a4.getClass().getSuperclass().getName());
Object obj1 = a1;
Object obj2= a4;
//Object[] obj3 = a1;//不可以,因为a1是int类型的数组!
Object[] obj4 = a4;//可以,因为String是一个Object
Object[] obj5 = a3;//可以,因为int[]是一个Object
System.out.println(a1);
System.out.println(a4);
System.out.println(Arrays.asList(a1));//按照Object转换a1,因此还是异常显示(jdk1.5)
System.out.println(Arrays.asList(a4));//按照Object[]转换a4,因此可以正常显示(jdk1.4)
printObj(a4);
printObj("zxy");
}
private static void printObj(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);
}
}
运行结果:
true
[I
java.lang.Object
[[I
java.lang.Object
[I@14318bb
[Ljava.lang.String;@ca0b6
[[I@14318bb]
[b, x, a]
b
x
a
zxy
ArrayList与HashSet的比较及HashCode的分析:
public class ReflectTest2 {
public static void main(String[] args) {
//ArrayList按照先后顺序存储
//Collection cl = new ArrayList();
Collection cl = new HashSet();
//存储时按照HashCode值来划分区域,存储到不同的区域,
//查找时,先算出对象的hashcode值,再到相应的区域寻找,这样就提高了查找效率
ReflectPoint r1 = new ReflectPoint(3,3);
ReflectPoint r3 = new ReflectPoint(4,5);
ReflectPoint r2 = new ReflectPoint(2,3);
ReflectPoint r4 = new ReflectPoint(3,3);
cl.add(r1);
cl.add(r2);
cl.add(r3);
cl.add(r4);
cl.remove(r1); //没有修改的时候,成功删除,size变为2
System.out.println(cl.size());
//当一个对象被存储进HashSet后,就不能更改这个对象中参与哈希值的字段了!
//会造成内存泄露!!!
cl.add(r1);
r1.y = 8;
cl.remove(r1);
//修改参与计算哈希值的字段后,删除失败!
//size仍然为3!长此以往,会造成内存泄露!
System.out.println(cl.size());
//没有重写HashCode方法时,size = 4
//重写hashCode方法后,size = 3
}
}
运行结果:

反射的作用:实现框架功能
调用别人的类,为工具类
自己的类被别人调用,为框架
//读取配置文件来实现创建对象
InputStream is = new FileInputStream("config.properties");
//读取键值对,用Properties比较方便
Properties prop = new Properties();
prop.load(is);
is.close(); //释放对象关联的系统资源
String className = prop.getProperty("className");
Collection cl = (Collection)Class.forName(className).newInstance();
当config.properties为ArrayList时:

运行结果:

当config.properties为HashSet时:

运行结果:

资源和配置文件的管理:
用类加载器的方式:
/*
* getRealPath()
* 一定要用绝对路径,但是该绝对路径不是硬编码,而是运算得出
*/
//读取配置文件来实现创建对象
//InputStream is = new FileInputStream("config.properties");//相对路径
//利用类加载器加载配置文件
//InputStream is = ReflectTest2.class.getClassLoader().getResourceAsStream("com/itheima/day1/config.properties");
//InputStream is = ReflectTest2.class.getResourceAsStream("config.properties");//简化的相对路径
InputStream is = ReflectTest2.class.getResourceAsStream("/com/itheima/day1/config.properties");//斜杆开头,需要绝对路径
总结:
1.静态导入:
静态导入只是在原来导入的基础上多加了一个static的修饰符,这样就可以直接导入该类中所有的静态方法,导入后可以直接调用静态方法,不需要在方法前添加类名
格式:import static java.lang.Math.*;
2.可变参数,增强的for循环
可变参数,就是一个方法可以有多个同类型的参数的缩写方式;
格式:func(int...args)//func方法可以有0~N个int参数
增强的for循环,可以将for循环简写
格式:for(int arg:args)//循环遍历args的每一个参数
3.自动装箱与拆箱&享元模式
自动装箱:将一个基本数据类型自动包装为对应的类对象
Integer x = 3;//将一个int类型数据3直接自动装箱为一个Integer对象x
自动拆箱:将一个基本数据包装类对象自动拆解为基本数据类型
System.out.println(x+12); //直接将Integer对象x自动拆箱为一个int类型数据
享元模式:
对于那些经常使用到的对象,建立专门的一块区域存储,后续再建立该对象,直接引用其地址即可,不需要重新创建对象,可 以节省内存开销.
具体实例:
String x = "hello";
String y = "hello";
//x与y的值相等,都指向hello所在的内存区域
4.枚举类
public enum TrafficLamp{
RED(30){//调用父类带参数的构造方法
//每个变量都是TrafficLamp父类的一个子类,因此需要实现父类的抽象方法
public TrafficLamp nextLamp()
{
return GREEN;
}
},
public abstract TrafficLamp nextLamp();
private int time;
private TrafficLamp(int time)
{
this.time = time;
}
}
}
5.反射
Reflection是Java 程序开发语言的特征之一,它允许运行中的 Java 程序对自身进行检查,或者说"自审",并能直接操作程序的内部属性。
先要得到字节码,三种方式:
注:字节码文件是经过编译器预处理过的一种文件,是JAVA的执行文件存在形式,它本身是二进制文件,但是不可以被系统直接执行,而是需要虚拟机解释执行,由于被预处理过,所以比一般的解释代码要快,但是仍然会比系统直接执行的慢。
- Class cls1 = Date.class;//得到Date类的字节码
- new Date().getClass() //
- 3.Class.forName("java.lang.String")//返回String类的字节码
得到反射类的构造方法:
Constructor<String> cons = String.class.getConstructor(StringBuffer.class);
//错误,不能这样传递,因为cons获得的构造方法必须带StringBuffer的参数
//String str = cons.newInstance("abc");
//通过得到的构造方法cons创建实例对象,通过newInstance();方法
String str = cons.newInstance(new StringBuffer("abc"));
得到反射类的成员变量:
Field fieldy = pt1.getClass().getField("y");//只能得到可见的参数
//filedy不是对象上的变量,而是类上的变量,因此用filedy.get(对象名)来获取其值;
System.out.println(fieldy.get(pt1));//得到具体实例对象中的成员变量值
Field fieldx = pt1.getClass().getDeclaredField("x");//可以看到private修饰的参数值
得到反射类的方法:
//getMethod():第一个参数为想要获取的方法名,第二个参数为方法接受的参数类型
Method methodAt = String.class.getMethod("charAt", int.class);
//使用该方法时,用invoke方法
System.out.println(methodAt.invoke(str1,1));
6.HashSet()使用注意事项:
修改参与计算哈希值的字段后,会导致删除失败!长此以往,会造成内存泄露!
参与计算哈希值的字段千万小心修改!
来源: http://blog.163.com/ljj1219@126/blog/static/143633241201461451655957/

浙公网安备 33010602011771号