枚举
什么是枚举?
枚举这个其实自己比较熟悉,因为曾经写过一些游戏,有些的注册了按键上下左右,这个上下左右就是用枚举来描述的。
其实一句话就是:枚举是定义自己特定的常量组合。
高级枚举技术?
枚举里面定义的是继承了Enum类的子类对象,枚举元素就是那些子类对象
如下代码:
public enum TrafficLamp{
//带有抽象方法的枚举
RED(43){
public TrafficLamp nextLamp(){
return GREEN;
}
},GREEN(44){
public TrafficLamp nextLamp(){
return YELLOW;
}
},YELLOW(12){
public TrafficLamp nextLamp(){
return RED;
}
};
public abstract TrafficLamp nextLamp();
private int time;
private TrafficLamp(int time){
this.time = time;
}
}
技术特点:
枚举就相当于一个类,其中也可以定义构造方法,成员变量,普通方法和抽象方法。
枚举元素必须位于枚举体中的最开始部分,枚举元素列表的后要有分号与其他成员分隔。把
枚举中的成员方法或变量放在枚举元素前面,编译器报告错误
带构造方法的枚举:
私有,枚举元素()
带方法的枚举:
用匿名类用枚举元素实现抽象方法
枚举只有一个成员时,就可以作为一种单例的实现方式
---------------------------------------------------------------------------------------------------------------------------------------------------
反射(reflection)
什么是反射?
反射就是把JAVA类中的各种成分映射成相应的java类。
例如,一个java类中用一个class类的对象表示,
一个类中的组成部分:成员变量 ,方法,构造方法,包等等信息也用一个个的java类来表示,
就像汽车是一个类,汽车中的发动机,变速箱等等也好似一个个类。
表示java类的class类显然要提供一系列的方法,
来获得其中的变量,方法,构造方法,修饰符,包等信息
这些信息就是用响应的实例对象来表示,他们是Field、method、contructor、package
其实反射在我的理解很简单,就是在运行期中取得对象属性,就是把java类中的各种成分映射成响应的java类。
教程中主要是讲解了Constructor Field Method 等的应用,因为有很多的新知识是代码中体现,所以现贴出代码
package cn.itcast.day1;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Arrays;
public class ReflectTest {
/**
* @param args
*/
public static void main(String[] args)throws Exception {
// TODO Auto-generated method stub
/*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.isPrimitive());
System.out.println(int.class.isPrimitive());
System.out.println(int.class == Integer.class);
System.out.println(int.class == Integer.TYPE);
System.out.println(int[].class.isArray());
上面为反射的简单应用演示*/
//new String(new StringBuffer("abc"));
Constructor<String> constructor1 = String.class.getConstructor(StringBuffer.class);//
//上面是获取对象的哪一个构造方法,在获取的方法中,传入参数的class对象即可
String str2 = constructor1.newInstance(new StringBuffer("abc"));
// 要传递同样类型的对象来用construtor获取的构造方法新建对象。
//新建的实例必须要强制转换,因为construtor对象很多,不能在编译期判断定
//class.newInstance()是内部封装了construtor然后调用无参数的构造方法
System.out.println(str2);
//获取字段
ReflectPoint pt1 = new ReflectPoint(3,5);
Field fieldY =pt1.getClass().getField("y");
System.out.println(fieldY.get(pt1));
//取得哪个对象的Y值。fieldY不是对象身上的变量,而是类上,要用它去取实例对象的值
Field fieldX = pt1.getClass().getDeclaredField("x");//取private的变量对象
fieldX.setAccessible(true);//暴力反射,符合那种拿不到的field对象
System.out.println(fieldX.get(pt1));
//changeStringField(pt1);
changeStringField2(pt1);
System.out.println(pt1);
//获取方法
String s2 = new String("char");
Method methodC = String.class.getMethod("charAt", int.class);
System.out.println(methodC.invoke(s2, 3));
//如果invoke()第一个参数为空,那么调用的是静态方法
//main方法
//TestArguments.main(new String[]{"111","222","333"});
Class argumentTest = Class.forName(args[0]);
Method method = argumentTest.getMethod("main", String[].class);
method.invoke(null, (Object)new String[]{"111","222","333"});
/*按照1.4的语法,数组中的每个元素对应一个参数,当把一个字符串数组
* 作为参数传递给invoke方法的时候,java就按1.4来处理把数组打散成
* 若干个单独的参数。
*
* 解决办法:
* mainMethod.invoke(null,new Object{new String[]{"111","222","333"}})
* mainMethod.invoke(null, (Object)new String[]{"111","222","333"})
*/
int []a1 = new int []{1,2,3};
int []a2 = new int [4];
int [][]a3 = new int [2][3];
String []a4 = new String []{"a","b","c"};
String []a5 = new String[5];
System.out.println(a1.getClass() == a2.getClass());
//System.out.println(a1.getClass() == a3.getClass());
System.out.println(a5.getClass() == a4.getClass());
//同维数,同类型的数组共享同一个class文件
System.out.println(a1.getClass().getName());
/*System.out.println(a1.getClass().getSuperclass().getName());
System.out.println(a4.getClass().getSuperclass().getName());
数组的父类是Object
*/
Object o1 = a1;
Object o2 = a3;
Object[] o3 = a3;
Object[] o4 = a4;
//Object[] o5 = a1; 这个不行,int数组类型(基本类型)不能赋值到Object上
System.out.println(a1);
System.out.println(a4);
//int[]被1.5当作一个元素传入和String[]的差异,Arrays.asList()可以测试
System.out.println(Arrays.asList(a1));
System.out.println(Arrays.asList(a4));
/*[[I@1f33675]
[a, b, c]*/
}
private static void changeStringField(ReflectPoint pt1) throws Exception{
// TODO Auto-generated method stub
Field [] field = pt1.getClass().getFields();
for(int i = 0 ;i < field.length;i++){
if(field[i].get(pt1) instanceof String){//判断
String s = (String)field[i].get(pt1);
s = s.replace('b', 'a');
field[i].set(pt1, s);
}
}
}
private static void changeStringField2(ReflectPoint pt1) throws Exception{
// TODO Auto-generated method stub
Field [] field = pt1.getClass().getFields();
for(Field f : field){
if(f.getType() == String.class){
String s = (String)f.get(pt1);
f.set(pt1, s.replace('l', 'a'));
}
}
}
}
class TestArguments{
public static void main(String[]args){
for(String arg:args){
System.out.println(arg);
}
}
}
反射的究竟有何作用?
主要是为了实现框架功能。
---------------------------------------------------------------------------------------------------------------------------------------------------
内省(Introspector)
什么是内省?
private static void setPropecties(Object pt1, String propertyName, Object value) throws IntrospectionException, IllegalAccessException, InvocationTargetException { //这时内省的类 PropertyDescriptor pd2 = new PropertyDescriptor(propertyName, pt1.getClass()); //propertyName是传入的要获取的元素,好像X,Y,age这类的, //pt1是获取class字节码对象,以便获取class对象的方法 Method methodSetX = pd2.getWriteMethod(); methodSetX .invoke(pt1, value); }
教程中还演示了利用apache的BeanUtil的类,教会了如何使用第三方包。这里省略。
---------------------------------------------------------------------------------------------------------------------------------------------------
注解
什么是注解?
注解的图解:
基本的注解类
@Override SOURSE 编译器识别复写 -----检查是否重写
@SuppressWarnings SOURSE 编译器识别警告----禁止警告
@Deprecated RUNTIME 运行期识别失效代码 扫描二进制码。----过期
自定义自己的注解类
package cn.itcast.day2;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/*元注解
元数据
元信息*/
import cn.itcast.day1.EnumTest;
/*@Retention(RetentionPolicy.SOURCE)
@Retention(RetentionPolicy.CLASS)
@Retention(RetentionPolicy.RUNTIME)
分别对应java源文件,class文件,内存中字节码
*/
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD,ElementType.TYPE})//Type描述接口,枚举,注解,class
//注解放置目标
public @interface ItcastAnnatation {
String color()default "blue";//设定自身属性,默认为蓝色,否则在使用注解的时候要传入参数
String value();
int[] arrayAttr()default {3,4,5};
EnumTest.TrafficLamp lamp() default EnumTest.TrafficLamp.RED;//枚举类别也可以定义
MetaAnnotation annotationAttr() default @MetaAnnotation("lhm");//也可以定义其他的注解类
Class gClass() default String.class; //Class类
}
注解类的使用
注解可以加在包,类,字段,方法,方法的参数以及局部变量上。
package cn.itcast.day2;
//这个其实是一个实际对象,贴上牌子
@ItcastAnnatation(color = "red",value="abc",gClass = Integer.class,
arrayAttr=1,annotationAttr=@MetaAnnotation("flx"))
//如果注解属性只有一个元素,这个属性值部分可以省略大括号
public class AnnatationTest {
/**
* @param args
*/
@SuppressWarnings("deprecation")
@ItcastAnnatation("xyz")//只有一个注解属性可以直接传入,或者多个属性时,可以导入默认值
public static void main(String[] args) {
// TODO Auto-generated method stub
System.runFinalizersOnExit(true);
if(AnnatationTest.class.isAnnotationPresent(ItcastAnnatation.class)){
ItcastAnnatation annotation
= (ItcastAnnatation)AnnatationTest.class.getAnnotation(ItcastAnnatation.class);
System.out.println(annotation.color());
System.out.println(annotation.arrayAttr().length);
System.out.println(annotation.value());
System.out.println(annotation.lamp().nextLamp().name());
System.out.println(annotation.annotationAttr().value());
System.out.println(annotation.gClass().getName());
//System.out.println(Arrays.asList( annotation.arrayAttr()));
//int数组传入的是一个int的父类对象object,在aslist方法中只存有1个元素
}
}
@Deprecated
public static void sayHello(){
System.out.println("hi,传智播客");
}
}
疑问:
其实看完了注解这一方面的知识我还有个疑问,因为上面介绍的基本注解类放置在特定的成员位置后,编译器会产生相对应的动作
这个动作是可以在注解类中定义还是编译器查看了标记后自己做出的处理?