枚举的介绍
枚举:
1. 为什么要有枚举
问题:要定义星期几或者性别的变量,该怎么定义呢?假设1-7分别表示星期一到星期日,单有人可能会写成int weekday=0;
枚举就是要让某个类型的变量的取值只能为若干个固定值中的一个,否则,编译器就会报错,枚举可以让编译器编译时就可以控制源程序中填写的非法值,普通变量的方式在开发阶段无法实现这一目标
枚举的基本应用
用普通类模拟枚举的实现原理
package com.itcast.day1;
public abstract class WeekDay
{
private WeekDay()
{
}
public finalstatic WeekDay SUN=newWeekDay()
{
@Override
public WeekDay nextDay()
{
return MON;
}
};
public finalstatic WeekDay MON=newWeekDay()
{
@Override
public WeekDay nextDay()
{
return SUN;
}
};
public abstractWeekDay nextDay();
{
}
/* public WeekDay nextDay()
{
if(this==SUN)
return MON;
else
{
return SUN;
}
}*/
@Override
public String toString()
{
returnthis==SUN?"SUN":"MON";
}
}
package com.itcast.day1;
public class EnumTest
{
public staticvoid main(String[] args)
{
WeekDayweekDay=WeekDay.MON;
System.out.println(weekDay.nextDay());//SUN
}
}
java的枚举的基本应用
public class EnumTest
{
public static void main(String[] args)
{
WeekDay1 weekDay=WeekDay1.MON;
System.out.println(weekDay.nextDay());//SUN
WeekDay weekDay2=WeekDay.FRI;//自动将枚举转换成string
System.out.println(weekDay2);//FRI
System.out.println(weekDay2.name());//FRI
System.out.println(weekDay2.ordinal());//排行 第5
System.out.println(WeekDay.valueOf("SUN"));//将字符串SUN转换成对象
System.out.println(WeekDay.values().length);//得到这个枚举数组的长度 76
}
//定义枚举
public enum WeekDay
{
SUN,MON,TUE,WED,THI,FRI,SAT;
}
}
实现带有构造方法的枚举
枚举也是一个类
package com.itcast.day1;
public class EnumTest
{
public staticvoid main(String[] args)
{
WeekDay1weekDay=WeekDay1.MON;
System.out.println(weekDay.nextDay());//SUN
WeekDayweekDay2=WeekDay.FRI;//自动将枚举转换成string
System.out.println(weekDay2);//FRI
System.out.println(weekDay2.name());//FRI
System.out.println(weekDay2.ordinal());//排行 第5
System.out.println(WeekDay.valueOf("SUN"));//将字符串SUN转换成对象
System.out.println(WeekDay.values().length);//得到这个枚举数组的长度 76
}
//定义枚举
public enumWeekDay
{
// SUN,MON,TUE,WED,THI,FRI,SAT;//元素列表必须定义在最前面
SUN(1),MON,TUE,WED,THI,FRI,SAT;//SUN(1) 加参数列表 调用有参数的构造参数
//MON() 表示调用默认的构造方法
//构造方法必须定义在元素列表后面
privateWeekDay()
{
System.out.println("first");
}
privateWeekDay(int day)
{
System.out.println("secod");
}
}
}
实现带有抽象方法的枚举
枚举只有一个成员时,就可以作为一种单例的实现方式
package com.itcast.day1;
import java.util.Date;
public class EnumTest
{
public staticvoid main(String[] args)
{
WeekDay1weekDay=WeekDay1.MON;
System.out.println(weekDay.nextDay());//SUN
WeekDayweekDay2=WeekDay.FRI;//自动将枚举转换成string
System.out.println(weekDay2);//FRI
System.out.println(weekDay2.name());//FRI
System.out.println(weekDay2.ordinal());//排行 第5
System.out.println(WeekDay.valueOf("SUN"));//将字符串SUN转换成对象
System.out.println(WeekDay.values().length);//得到这个枚举数组的长度 76
}
//定义枚举
public enumWeekDay
{
// SUN,MON,TUE,WED,THI,FRI,SAT;//元素列表必须定义在最前面
SUN(1),MON,TUE,WED,THI,FRI,SAT;//SUN(1) 加参数列表 调用有参数的构造参数
//MON() 表示调用默认的构造方法
//构造方法必须定义在元素列表后面
privateWeekDay()
{
System.out.println("first");
}
privateWeekDay(int day)
{
System.out.println("secod");
}
}
//交通灯,带上抽象方法的枚举
透彻分析反射的基础_Class类
反射的基石—> Class类
1. java程序中的各个JAVA类属于同一事物,描述这类事物到的JAVA类名就是Class
2. 对比提问:众多的人用什么类表示?众多的JAVA类用什么表示?
人-> Person
JAVA类->Class
3. 对比提问:Person类代表人,它的实例对象就是张三,李四这样一个个具体的人,Class代表Java类,它的各个实例对象又分别是什么呢?
1. 对应各个类在内存中的字节码。例如Person类的字节码,ArrayList类的字节码,等等
2. 一个类被加载器加载到内存中,占用一片存储空间,这个空间里面的内容就是类的字节码,不同的类字节码是不同的,所以他们在内存中的内容是不同的,这一个个的空间可分别用一个个的对象来
如何得到各个字节码对应的实例对象(Class类型)
1. 类名.class,例如,System.class
2. 对象.getClass(),例如,new Date().getClass();
3. Class.foName(“类名”);,例如,Class.forName(“java.util.Date”);
9个预定义Class实例对象:
1. 查看Class.isPrimlative方法的帮助
2. Int.claas==Integer.class fales
package com.itcast.day1;
public class ReflectTest
{
public staticvoid main(String[] args) throws Exception
{
Stringstr1="abc1";
Classclsl=str1.getClass();
Classcls2=String.class;
Classcls3=Class.forName("java.lang.String");
System.out.println(clsl==cls2);//true
System.out.println(clsl==cls3);//true
System.out.println(clsl.isPrimitive());//false不是基本类型
System.out.println(int.class==Integer.class);//false
System.out.println(int.class==Integer.TYPE);//true
System.out.println(int[].class.isArray());//true
//数组类型的class实例对象用class.isArray()
//总之,只要是在源程序出现的类型都有各自的的Class实例对象,如int[],void
}
}
public enumTrafficLamp
{
//相等于new子类的实例对象,并且调用父类的有参数的构造方法
RED(30){
@Override
public TrafficLamp nextLamp()
{
//红灯以后返回绿灯
return GREEN;
}
},
GREEN(45){
@Override
public TrafficLamp nextLamp()
{
//绿灯以后黄
return YELLOW;
}
},
YELLOW(5){
@Override
public TrafficLamp nextLamp()
{
//黄灯以后红灯
return RED;
}
};
publicabstract TrafficLamp nextLamp();
privateint time;//每个灯都拥有自己的时间
privateTrafficLamp(int time)
{
this.time=time;
}
}
}
理解反射的概念
反射
1. 反射就是把Java类中的各种成分映射成相应的的java类。例如,一个java类中用一个Class类的对象来表示,一个类中的组成部分:成员变量,方法,构造方法,包等信息也用一个个的Jvaa类来表示,就像汽车是一个类,汽车中的发动机,变速箱等等也是一个个的类,表示java类的Class类显然要提供一系列的方法,来获取其中的变量,方法,构造方法,修饰符,包等信息,这些信息就是用相应的类的实例对象来表示,他们的Field,Method,Contructor,Package等等
2. 一个类中的每个成员都可以用到相应的反射API类的一个实例对象来表示,通过调用Class类的方法可以得到这些实例对象后,得到这些实例后怎么用?
构造方法的反射应用
Constructor类
1. Constructor类代表某个类中的一个构造方法
2. 得到某个类的所有构造方法
例子:Constructor construtors[]=Class.forName(“java.lang.String”).getConstrutors();
3. 得到某一个构造方法:
例子: Constructors constructors=Class.forName(“java.lang.String”).getContstructor(StringBuffer.class);
//获得方法是要用到的类型
4. 创建实例对象
1. 通常方式:String str=new String(new StringBufferf(“abc”));
2. 反射方式:String str=(Strtring)constructor.newInstance(new StringBuffer(“abc”));
//调用方法时要用到上面相同的类型的实例对象
5. Class.newInstance()方法
例子:String obj=(String)Classs.forName(“java.lang.String”).newInstance();
该方法内部先得到默认的构造方法,然后该构造方法创建实例对象
该方法内部的具体代码是怎样写的呢?用到了缓冲机制来保存默认的构造方法的实例对象
public staticvoid main(String[] args) throws Exception
{
String.class.getConstructor(StringBuffer.class);//值接受一个参数的的构造方法
String str2=new String(newStringBuffer("jj"));
// Constructorconstructor=String.class.getConstructor(StringBuffer.class)
Constructorconstructor=Class.forName("java.lang.String").getConstructor(StringBuffer.class);
String str3=(String)constructor.newInstance(newStringBuffer("aaa"));
}
成员变量的反射
Field类
1. Field类代表某个类中的一个成员变量
2. 演示用eclipse自动生成Java类的构造方法
3. 问题:得到的Field都系是对应到类上面的成员变量,还是对应到对象上的成员变量?类只有一个,而该类的实例对象有多个,如果是与对象关联,哪关联的是哪个对象呢?所以字段fieldX代表的是X的定义,而不是具体的X变量
实例代码:
public class ReflectPoint
{
private intx;
public inty;
public ReflectPoint(int x, int y)
{
super();
this.x = x;
this.y = y;
}
}
package com.itcast.day1;
import java.lang.reflect.*;
public class ReflectTest
{
public staticvoid main(String[] args) throws Exception
{
ReflectPoint fp=new ReflectPoint(3,5);
Field fieldY=fp.getClass().getField("y");//代表变量,不代表值
System.out.println(fieldY);
System.out.println(fieldY.get(fp));//从那个对象上取出y
//对于私有属性:设置可见----->设置可以访问
FieldfieldX=fp.getClass().getDeclaredField("x");//面对私有的变量,看不见,就用声明的字段方法获取
fieldX.setAccessible(true);//暴力反射,不管对方同意不?设置可以可以访问
System.out.println(fieldX.get(fp));
}
}
成员变量反射的综合案例
作业:将任意一个对象中的所有String类型的成员变量所对应的字符串内容的”b“改成”a”;
package com.itcast.day1;
public class ReflectPoint
{
private intx;
public inty;
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;
}
}
package com.itcast.day1;
import java.io.File;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
public class ReflectTest
{
public staticvoid main(String[] args) throws Exception
{
changeStringValue(fp);
System.out.println(fp);//aall:aasketaall:itcast
}
private staticvoid changeStringValue(Object obj) throws Exception
{
Field[]fields=obj.getClass().getFields();
for(Field field:fields)
{
if(field.getType()==String.class)
{
StringoldValue=(String) field.get(obj);
StringnewValue=oldValue.replace('b','a');//替换
field.set(obj,newValue);//设置对象上的那个值
}
}
}
}
成员方法的反射
Method类
1. Method类代表某个类中的一个成员方法
2. 得到类中的某一个方法:
例子:
Method charAt=Class.forName(“java.lang.String”).getMethod(“charAt”,int.class);
3. 调用方法:
通常方式:System.out.println(str.charAt(1));
反射方式:System.out.println(charAt.invoke(str,1));
如果传递给Method对象的invoke()方法的一个参数为null,这有着什么样的意义呢?说明该Method对象对应的是一个静态方法!
4. jdk1.4和jdk1.5的invoke方法的区别
jdk1.5:public Object invoke(Object obj,Object…args)
Jdk1.4: publicObject invoke(Object obj,Object[] args),即按照jdk1.4的语法
需要将与各数组作为参数传递给invoke方法时,数组中的每个元素分别对应被
public static void main(String[] args) throws Exception
{
Stringstr1="abc1";
//调用str1.charAt(1);
Method methodCharAt=String.class.getMethod("charAt",int.class);
System.out.println(methodCharAt.invoke(str1,1));//调用charAt方法,此得到b
//methodCharAt.invoke(null,1) 调用静态方法,对象为null
System.out.println(methodCharAt.invoke(str1,new Integer[]{2}));//jdk.1.4调用方式
}
对接收数组参数的成员方法进行反射
用反射的方式执行某个类中的main方法
1. 目标:
写一个程序,这个程序能够根据用户提供的类名,去执行类中的main方法
普通方式调用完后,
2. 问题
启动java程序的main方法参数是一个字符串数组,即public static void main(String[] args),通过反射的方式来刁永红这个main方法时,如果和为invoke方法传递参数呢?按jdk1.5的语法,整个数组是一个参数,而按jdk1.4的语法,数组中的每个元素对应一个参数,当把一个字符串作为参数传递给invoke方法时,javac会到底按照那种语法进行执行呢?jdk1.5版本肯定要兼容jdk1.4的语法,会按照jdk1.4的语法进行处理,即把数组打散成为若干个单独的参数。所以,在给main方法传递参数时,不能使用代码mianMethod.invoke(null,new String[]{“xxx”}),javac只能把它的那个在jdk1.4的语法进行理解,而不把它当做jdk1.5的预付解释,因此会出现参数类型不对的问题
5. 解决办法:
mainMethod.invoke(null,new Object[]{new String{“xxx”}});
mainMethod.invoke(null,(Object)new String[]{“xxx”});,编译器会做特殊处理,编译时不会把参数当做数组看待,也就不会进数组打散成若干参数了
package com.itcast.day1;
import java.io.File;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
public class ReflectTest
{
public static void main(String[] args) throws Exception
{
//TestArguments.main(new String[]{"acb","hhf","jdj"});
//用反射的方式调用
String startingClassName=args[0];
MethodmainMethod=Class.forName(startingClassName).getMethod("main",String[].class);
//现将字符串打包成一个Object对象数组的第一个对象
mainMethod.invoke(null, new Object[]{newString[]{"acb","hhf","jdj"}});
//或者
// mainMethod.invoke(null,(Object)newString[]{"acb","hhf","jdj"});
}
privatestatic void changeStringValue(Object obj) throws Exception
{
Field[]fields=obj.getClass().getFields();
for(Fieldfield:fields)
{
if(field.getType()==String.class)
{
StringoldValue=(String) field.get(obj);
StringnewValue=oldValue.replace('b','a');//替换
field.set(obj,newValue);//设置对象上的那个值
}
}
}
}
class TestArguments
{
publicstatic void main(String[] args)
{
for(Stringarg:args)
{
System.out.println(arg);
}
}
}
数组与Object的关系及其反射类型
数组(具有相同的元素类型,和相同的纬度)的字节码是同一个对象
int[] a1=newint[]{1,2,3};
int[]a2=new int[4];
int[][]a3=new int[2][3];
String[] a4=newString[]{"a","b","c"};
System.out.println(a1.getClass()==a2.getClass());//true
//System.out.println(a1.getClass()==a4.getClass());//报错
System.out.println(a1.getClass().getName());//[I 表示数组 整数
System.out.println(a1.getClass().getSuperclass());//class java.lang.Object
System.out.println(a1.getClass().getSuperclass().getName());
/*
父类名 java.lang.Object
*/
Object obj1=a1;
Object obj2=a4;
// Object[] obj3=a1;不能转换
Object[] obj4=a3;
Object[] obj5=a4;
System.out.println(a1);//[I@43ef9157
System.out.println(Arrays.asList(a1));//[[I@331f2ee1]
System.out.println(Arrays.asList(a4));//[a, b, c]
数组的反射应用
数组的反射
1. 具有相同的纬度和元素类型的数组属于同一个类型,即具有相同的Class实例对象
2. 代表数组的Class实例对象的getSuperClass()方法返回的父类为Object类对应的Class
3. 基本类型的一维数组可以被当做Objetc类型使用,不能当做Objetc[] 类型使用;非基本类型的一维数组,即可以当做Object类型的使用,又可以当做Object[]类型使用
4. Arrays.asList()方法处理int[]和String[]时的差异
5. Array工具类用于完成对数组的反射操作
思考题:怎么得到数组的元素类型?没办法
public staticvoid main(String[] args) throws Exception
{
String[] aa=new String[]{"a","b","c"};
Object obj=aa;
printObject(obj);
printObject("jjd");
}
private staticvoid printObject(Object obj)
{
Classclasszz=obj.getClass();
if(classzz.isArray())//是数组
{
int len=Array.getLength(obj);//得到长度
for(inti=0;i<len;i++)
{
System.out.println(Array.get(classzz, i));
}
}
else
{
System.out.println(obj);
}
}