------------Android培训、Java培训、期待与你交流------------
1.枚举
枚举是JDK1.5出现的新特性,其实就是一个特殊的类,其中也可以定义构造方法,成员变量,普通方法和抽象方法。枚举元素必须位于方法体中最开始的部分,枚举元素列表需要用分号和其他成员分隔。把枚举重的成员方法或变量等放在枚举元素的前面,编译器会编译失败。
用普通类如何实现枚举功能,定义一个Weekday的类来模拟枚举功能。首先要有私有的构造方法,每个元素分别都是一个公有的静态成员变量表示,可以有若干公有方法或抽象方法。这是带构造方法的枚举, 构造方法必须定义成私有的,枚举元素MON和MON()的效果一样,都是调用默认的构造方法。如果希望元素调用带有参数的构造函数初始化,可以在元素名后面加上参数。带方法的枚举可以通过定义枚举TrafficLamp这个例子来验证,实现普通的next方法, 实现抽象的next方法:每个元素分别是由枚举类的子类来生成的实例对象,这些子类采用类似内部类的方式进行定义。增加上表示时间的构造方法。具体实现代码如下:
本人比较懒,把两个例子写到一个里去了~~
public class EnumTest
{
public static void main(String[] args)
{
/*对应WeekDay01中的注释部分
WeekDay01 weekDay = WeekDay01.MON;
System.out.println(weekDay.nextDay());
*/
/*
WeekDay weekDay02 = WeekDay.FRI;
System.out.println(weekDay02);
System.out.println(weekDay02.name());
System.out.println(weekDay02.ordinal());//所在位置的index
System.out.println(weekDay02.getClass());
System.out.println(WeekDay.valueOf("SUN").toString());
System.out.println(WeekDay.values().length);
*/ //
//
//
} //
//\\//
public enum WeekDay
{
//均为静态变量
SUN,MON(1),TUE(1),WED,THU(1),FRI,SAT;//元素类表必须置于所有方法之上,
//如果列表后边有内容最后需要加分号,如果没有可不加。
//加括号是为了调用有参数的构造方法
private WeekDay()//只能是私有类型的构造函数
{
System.out.println("first");
}
private WeekDay(int day)
{
System.out.println("second");
}
}
public enum TrafficLights
{
//new一个子类的实例对象<RED>,并且调用父类(TrafficLights)有参数的
//构造方法private TrafficLights(int time)<(30)>
RED(30)
{
public TrafficLights nextLamp()
{
return GREEN;
}
} ,
GREEN(45)
{
public TrafficLights nextLamp()
{
return YELLOW;
}
} ,
YELLOW(5)
{
public TrafficLights nextLamp()
{
return RED;
}
};
public abstract TrafficLights nextLamp();
private int time;
private TrafficLights(int time)
{
this.time = time;
}
}
}
public abstract class WeekDay01
{
private WeekDay01(){}//私有的构造方法
public final static WeekDay01 SUN = new WeekDay01()
{ //匿名内部类
public WeekDay01 nextDay()//覆盖父类的方法
{
return MON;
}
};
public final static WeekDay01 MON = new WeekDay01()
{ //匿名内部类
public WeekDay01 nextDay()
{
return SUN;
}
};
/*对应EnumTest中注释部分
public final static WeekDay TUE = new WeekDay();
public final static WeekDay WED = new WeekDay();
public final static WeekDay THE = new WeekDay();
public final static WeekDay FRI = new WeekDay();
public final static WeekDay SAT = new WeekDay();
public WeekDay nextDay()
{
if(this == SUN)
{
return MON;
}
else
{
return SUN;
}
}
*/
public abstract WeekDay01 nextDay();//因为是抽象方法,所以其所在的类也必需要变成是抽象的
public String toString()
{
return this==SUN?"SUN":"MON";
}
}
2.反射
反射就是把Java类中的各种成分映射成相应的java类。
例如:一个java类用一个Class类的对象来表示,一个类的组成部分:成员变量,方法,构造方法,包等信息也用一个个的Java类来表示,就像汽车是一个类,汽车中的发动机,变速箱等等也是一个个的类。表示java类的Class类显然要提供一系列的方法,来获得其中的变量,方法,构造方法,修饰符,包等
信息,这些信息就是用相应类的实例对象来表示,他们是Field,Method,Constructor,Package等。
Java类用于描述一类事物的共性,该类事物有什么属性,没有什么属性,至于这个属性的值是什么,则是由这个类的实例对象来确定的,不同的实例对象有着不同的属性值。java程序中的各个java类,它们是否属于同一类事物,是不是可以用一个类来描述这类事物呢?这个类的名字就是Class,要注意与小写class关键字的区别。
字节码:当在源程序中用到了Person这个类的时候,首先要从硬盘上把这个类的二进制代码加载到内存中,才能去创建一个个的对象,当程序中用到了多个类时候,内存中就会有多个相应的字节码,每一个字节码就是Class的实例对象总之,只要是在源程序中出现的类型,都有各自的Class实例对象。
Person p1 = new Person();
Person p2 = new Person();
Class cls1 = Date.class(内存中)Date类字节码1;
Class cls2 = Person.calss(内存中)Person类字节码2;
获取字节码的方式(3种)
1.对象.getClass() 比如p1.getClass() / new Date().getClass()
2.Class.forName("java.lang.String")-->静态方法
<两种情况>1.此类的字节码已经加载到内存中了,用时可直接返回
2.此类的字节码还没有加载到内存中,便用类加载器将其加载到内存中缓存起来
3.类名.class 比如System.class
有关以上内容的一些具体实现代码:
import java.lang.reflect.*;
//import java.lang.reflect.Field;
//import java.lang.reflect.Method;
public class ReflectTest
{
public static void main(String[] args) throws Exception
{
String str1 = "abc";
Class cls1 = str1.getClass();
Class cls2 = String.class;
Class cls3 = Class.forName("java.lang.String");
System.out.println(cls1==cls2);
System.out.println(cls2==cls3);
System.out.println(cls1==cls3);
//System.out.println(cls1.toString());
System.out.println(cls1.isPrimitive());//判断是否是基本类型的字节码
System.out.println(int.class.isPrimitive());//int型为基本类型的字节码
System.out.println(int.class==Integer.class);//int和Integer的类型不同
System.out.println(int.class==Integer.TYPE);
System.out.println(int[].class.isPrimitive());//数组不是原始类型
System.out.println(int[].class.isArray());
/*================================华丽的分割线====================================*/
//new String(new StringBuffer("abc"));
//新new一个对象,调用了StringBuffer构造方法
//用反射的方式实现形同的效果
Constructor constructor1 = String.class.getConstructor(StringBuffer.class);
//Constructor类型的对象,此句中的StringBuffer表示选择哪种构造方法
//constructor1此时只是一堆存在内存中没有实例化的二进制代码
//(String类中StringBuffer构造方法的字节码),下一条语句才会进行实例化
String str = (String)constructor1.newInstance(new StringBuffer("abc"));
//此句中的StringBuffer表示用此构造方法是需要传一个StringBuffer进去
/*
* 泛型的应用省去了类型转换之苦
Constructor<String> cons = String.class.getConstructor(StringBuffer.class);
String strr = cons.newInstance(new StringBuffer("abcd"));
*/
System.out.println("str:"+str);
System.out.println(str.charAt(1));
Object obj = constructor1.newInstance(new StringBuffer("abc"));
System.out.println("obj:"+obj);
System.out.println(obj.toString());
/*================================华丽的分割线====================================*/
/*//成员变量的反射
* Field类代表某一个类中的一个成员变量
* 以下代码对应ReflectPoint.java文件
*/
ReflectPoint pt1 = new ReflectPoint(3,5);
Field fieldY = pt1.getClass().getField("y");//只用于可见变量
//此时fieldY只是代表一个实例化的变量,不代表一个具体的值
//也就是说他不是对象身上的变量,而是类上的变量,需要实例化才有值
System.out.println(fieldY.get(pt1));
//需要用get方法来获取指定变量的值
Field fieldX = pt1.getClass().getDeclaredField("x");//可见变量和非可见变量均可
fieldX.setAccessible(true);//暴力反射
System.out.println(fieldX.get(pt1));
/*================================华丽的分割线====================================*/
//成员变量的反射
changeStringValue(pt1);
System.out.println("pt1:"+pt1);
/*================================华丽的分割线====================================*/
//成员方法的反射
//利用反射的方法调用字符串中的字符
Method methodCharAt = String.class.getMethod("charAt" , int.class);
//调用Str1.charAt(1)中的字符b
System.out.println(methodCharAt.invoke(str1 , 1));
System.out.println(methodCharAt.invoke(null , 1));//若第一个参数为null,说明该方法为静态
//按照JDK 1.4 version的方法调用str1中的字符c
//Object[]{2}) object类型的数组,里面只有一个Integer对象2,数组长度为1
System.out.println(methodCharAt.invoke(str1, new Object[]{2}));//JDK 1.4 version
/*================================华丽的分割线====================================*/
//用反射方式执行某个类中的main方法,为什么?
//CodeSegment
/*
String startingClassName = args[0];
Method mainMethod = Class.forName(startingClassName).getMethod("main", String[].class);
mainMethod.invoke(null, new String[]{"111","222","333"});
调用此段代码是会出现参数个数不对的异常,原因是new String[]{"111","222","333"}在JDK1.5+
中为了兼容JDK1.4,JVM在接受此数组是默认为object的数组,进行拆包从而不会当成一个参数,
此数组会被打开数组中的元素分别作为一个参数,从而有三个参数
解决办法:
1.再次打包
new Object[]{new String[]{"111","222","333"}}
即使String数组被打开,仍然是一个Object数组
2.(Object)new String[]{"111","222","333"}
对编译器指明此参数为一个对象,而不是数组,不需要进行拆包
*/
//main方法是静态方法,不需要传递对象
//目标:根据用户提供的类名,去执行该类中的main方法
TestArguments.main(new String[]{"111","222","333"});
/*================================华丽的分割线====================================*/
}
private static void changeStringValue(Object obj) throws Exception
{
//将对象中所有的String类型的变量进行扫描
Field[] fields = obj.getClass().getFields();
//先得到obj所属的那份字节码,再得到所属的字段,对字段进行迭代
for(Field field : fields)
{
//if(field.getType().equals(String.class))不建议使用
//因为只需要得到object中String类型的字段,所以将字段中的所有元素的类型通过
//field.getType()方法利用迭代方式逐次
//和String类型的字节码进行比较,相同的保留,不同的舍去
if(field.getType() == String.class)//字节码要用等号比较,专业
{
String oldValue = (String)field.get(obj);//得到字段中String类型元素
String newValue = oldValue.replace('b', 'a');//将字符串中目标字符进行替换
field.set(obj, newValue);//将生成的新字符串赋值给obj
}
}
}
}
class TestArguments
{
public static void main(String[] args)
{
for(String arg : args)
{
System.out.println(arg);
}
}
}