黑马程序员-Java基础加强-Java高新技术
一、eclipse及IDE开发工具介绍:
IDE开发工具都支持使用工程化方式 管理一个项目的程序开发过程。一般来说一个相对独立的项目就是一个工程。一个项目中涉及多个Java文件,资源文件等用一个工程进行管理。 一个workspace 可以包含多个 project ,一个workspace 保留了 Eclipse 的一套环境选项的配置。如果要为Eclipse再配置一套环境选项,可以再创建一个workspace。
所以:eclipse 工作台中的所有工程继承工作台的配置,其中某个工程也可以覆盖工作台的配置,Java面向对象的思想。
1、在Eclipse 中如何设置快捷键:
Windows->Preferences->General -> Editors ->Key (Content Assist:内容助理) 这样的设置对一个工作台中的所有项目应用。
注意:设置快捷键时,要解除原来的绑定关系。
2、设置代码模版:如输入syso,则可以自动补全System.out.println();
Windows->Preferences->General -> Editors ->Templates
3、在MyEclipse如何调试:
右键->Debug as 便会跳到调试透视图。观看一个变量的值:右键->watch
关于透视图:很多小窗口的集合。Eclipse 将很多功能小窗口集合成一个透视图,作用于相应的功能。
4、配置Eclipse 中的编译和运行环境:
Windows->Preferences->java ->Complier 配置Java程序编译版本
->Installed JREs 配置Java程序运行版本
高版本的Java能运行低版本的Java。 而低版本的Java不能运行高版本的Java。
二、Java 5新特性:
1、Java 5 的静态导入与编译器语法设置:
1 import static java.lang.Math.abs; 2 public class StaticImport { 3 public static void main(String[] args) { 4 //求两个数的最大值; 5 System.out.println(Math.max(3, 6)); 6 //通过静态导入Math中的abs方法。则可以直接调用abs方法 7 //相当于Math.abs() ; 8 System.out.println(abs(1-9)); 9 } 10 }
2、可变参数:
当一个方法要处理的参数不确定时,则可以设置可变参数——> 参数类型... 变量名 ;而且设定可变参数只能放一个方法传递参数集的最后。
1 public class ValueParameter { 2 public static void main(String[] args) { 3 System.out.println(add(1,2,3,4,5)); 4 System.out.println(add(1,3)); 5 } 6 // 实现若干个参数的累加 7 // 只能放置最后。 8 public static int add(int... arg){ 9 int sum = 0 ; 10 for (int i = 0 ; i < arg.length ; i++) { 11 sum += arg[i] ; 12 } 13 return sum ; 14 } 15 }
如:add(int... arg , int x ) 是错误的。
3、foreach 循环
语法: for(type 变量名: 集合变量名) { }
注意:
1、迭代变量必须在()中定义。
2、集合变量可以是数组或实现Iterator接口的集合类。
1 public static int add(int... arg){ 2 int sum = 0 ; 3 for(int x : arg){ 4 sum+=x ; 5 } 6 return sum ; 7 }
4、基本数据类型的自动拆箱和装箱以及FlyWeight设计模式
public class AutoBox { public static void main(String[] args) { // 在1.5以前:一下操作是不行的。 Integer iObj = 3 ; // 在1.5以后,自动装箱:将基本数据类型int,自动封装给一个Integer对象 // 类似于:Integer iObj = new Integer(3) ; System.out.println(iObj + 10); /* 自动拆箱:如上操作相当于: iObj = new Integer(iObj.intValue() + 10) */ Integer i1 = 13 ; Integer i2 = 13 ; System.out.println(i1==12); /* 享元设计模式:flyweight 有很多个小对象,他们有很多相同的属性,那么将它们封装成一对象,相同的属性称为 内部属性;不同的属性作为方法的参数。 */ } }
5、枚举
为什么要有枚举:就是为了让某个类型的变量的取值只能为若干个固定值中的一个,否则编译器就会报错。枚举可以让编译器在编译时就可以控制源程序中填写的非法值,普通变量的方式在开发阶段无法实现这一目标。
如:要定义星期几或性别变量时,假设用0-6分别表示星期天、星期一到星期六。但是有些人这会将星期一到星期天用1-7表示。
用普通类模拟枚举的实现原理:
1 public class EnumTest { 2 public static void main(String[] args) { 3 WeekDay Sun = WeekDay.Sun ; 4 System.out.println(Sun.nextDay()); 5 } 6 } 7 //普通类模拟枚举的实现原理 8 class WeekDay { 9 // 不让外部创建WeekDay 对象 10 private WeekDay() { 11 } 12 public final static WeekDay Sun = new WeekDay() ; 13 public final static WeekDay Mon = new WeekDay() ; 14 // 采用抽象方法定义nextDay就将大量的if.else语句转移成一个个独立的类 15 public WeekDay nextDay() { 16 if (this == Sun) { 17 return Mon ; 18 } 19 else{ 20 return Sun ; 21 } 22 } 23 public String toString(){ 24 return this == Sun ? "Sun" : "Mon" ; 25 } 26 }
不过nextDay中的if.else操作比较多,所以为了取消if.else 操作,则可以用抽象类,然后定义内部类:
1 //普通类模拟枚举的实现原理 2 public abstract class WeekDay { 3 // 不让外部创建WeekDay 对象 4 private WeekDay() { 5 } 6 public final static WeekDay Sun = new WeekDay(){ 7 public WeekDay nextDay(){ 8 return Mon ; 9 } 10 }; 11 public final static WeekDay Mon = new WeekDay(){ 12 public WeekDay nextDay(){ 13 return Sun ; 14 } 15 }; 16 // 采用抽象方法定义nextDay就将大量的if.else语句转移成一个个独立的类 17 public abstract WeekDay nextDay() ; 18 public String toString(){ 19 return this == Sun ? "Sun" : "Mon" ; 20 } 21 }
总结:采用抽象方法定义nextDay就将大量的if.else语句转移成一个个独立的类。
如何创建枚举呢?方法如下:
1、创建一个最简单的枚举:
1 public class EnumTest { 2 public static void main(String[] args) { 3 WeekDay weekday = WeekDay.SUN ; 4 // 打印weekday的名字 5 System.out.println(weekday); 6 System.out.println(weekday.name()); 7 // 打印weekday在WeekDay中的角标 8 System.out.println(weekday.ordinal()); 9 // 获取指定的值 10 System.out.println(WeekDay.valueOf("MON")); 11 // 获取所有的值 12 System.out.println(WeekDay.values().length); 13 } 14 // 创建一个枚举应用 enum类 15 public enum WeekDay { 16 SUN,MON,TUE,WED,THI,FRI,SAT 17 } 18 }
上述代码是在类中创建内部类。创建枚举的关键字是:enum。
2、创建一个带有构造方法的枚举:
1 // 创建一个枚举应用 enum类 2 public enum WeekDay { 3 SUN,MON,TUE,WED,THI,FRI,SAT(6) ; 4 // 构造方法必须位于元素列表之后;如果元素之后有方法,则必须使用";" 5 // 而且构造方法必须用private 修饰。 6 // 当调用枚举类时,初始化枚举静态元素时,SUN-FRI则会调用private WeekDay(){} 构造方法。 7 // 而SAT则会调用private WeekDay(int day){} 构造方法 8 private WeekDay(){} 9 private WeekDay(int day) {} 10 }
3、带有抽象方法的枚举:
1 // 交通灯 2 public enum TrafficLamp{ 3 // new子类的对象,调用父类的构造方法。 4 RED(40){ 5 public TrafficLamp nextLamp() { 6 return GREEN ; 7 } 8 }, 9 GREEN(60){ 10 public TrafficLamp nextLamp() { 11 return YELLOW ; 12 } 13 }, 14 YELLOW(5){ 15 public TrafficLamp nextLamp() { 16 return RED ; 17 } 18 }; 19 // 抽象方法: 20 public abstract TrafficLamp nextLamp() ; 21 // 定义交通灯的时间,成员变量 22 private int time ; 23 private TrafficLamp(int time) { 24 this.time = time ; 25 } 26 }
总结:枚举只有一个成员时,就可以作为一种单例的实现方式。
6、注解
注解相当于一种标记,在程序中加入了注解就等于为程序打上了某种标记,javac编译器开发工具和其他程序可以用反射来了解你的类以及各种元素上无何标记,看你有什么标记就干相应的事标记可以加在包,类,字段。方法,方法参数以及局部变量上。
几个基本的注解:
@Override 、@SuppressWarnings、@Deprecated 分别表示提醒 方法是否覆盖父类方法、提醒编译器做某种操作、标识方法过时。
1 public class AnnotationTest { 2 3 // 注解:提醒编译器跳过过时方法提醒。 4 @SuppressWarnings("deprecation") 5 public static void main(String[] args) { 6 System.runFinalizersOnExit(true); 7 } 8 // 提醒该方法过时了、 9 @Deprecated 10 public static void sayHello(){ 11 System.out.println("hello , world"); 12 } 13 // 提醒 方法是否覆盖父类方法。 14 @Override 15 public boolean equals(Object obj) { 16 return false; 17 } 18 }
每一个注解是一个类,注释的应用结构:
1、注释类:@interface A {
}
2、应用了"注释类"
@A
class B {
}
3、对"应用了注解类的类"进行反射操作的类
class C {
B.class.isAnnotionPersent(A.class) ;
A a = B.class.getAnnotion(A.class) ;
}
创建一个注释类:
1 @Retention(RetentionPolicy.RUNTIME) 2 @Target({ElementType.TYPE,ElementType.METHOD}) 3 public @interface ItcastAnnotation { 4 5 }
其中,@Retention、@Target是元注解,即注解的注解。
@Retention 其三种取值:
RetetionPolicy.SOURECE :Java源文件
RetetionPolicy.CLASS : class文件
RetetionPolicy.RUNTIME : 内存中的字节码
那么 @Override 在Java源文件阶段、@SuppressWarnings 在Java源文件阶段、@Deprecated 在内存中的字节码中 。
@Target 设置注解的目标:ElementType.TYPE : class 上、ElementType.METHOD : 方法上
通过反射的方式获取注解类:
1 @ItcastAnnotation() 2 public class AnnotationTest { 3 4 // 注解:提醒编译器跳过过时方法提醒。 5 @SuppressWarnings("deprecation") 6 public static void main(String[] args) { 7 System.runFinalizersOnExit(true); 8 9 10 // 检查该类上是否有@ItcastAnnotation 注解: 11 if (AnnotationTest.class.isAnnotationPresent(ItcastAnnotation.class)) { 12 ItcastAnnotation annotation = (ItcastAnnotation)AnnotationTest.class.getAnnotation(ItcastAnnotation.class) ; 13 System.out.println(annotation); 14 15 } 16 17 } 18 }
添加元素属性:
@Retention(RetentionPolicy.RUNTIME) @Target({ElementType.TYPE,ElementType.METHOD}) public @interface ItcastAnnotation { // 为注解增加基本属性 : 默认为bule // 如果只有一个value属性要填写,则可以省略 "value=" String color() default "blue"; String value() ; // 为注解增加高级属性 /* 数组类型的属性: 如果只有一个属性,则"{}" 可以省略 */ int[] arrayAttr() default {2,3,4}; /* 枚举类型的属性: */ EnumTest.TrafficLamp lamp() default EnumTest.TrafficLamp.GREEN ; }
应用注解:
1 @ItcastAnnotation(color="red",value = "haha",arrayAttr={1,2,3}) 2 public class AnnotationTest { 3 4 // 注解:提醒编译器跳过过时方法提醒。 5 @SuppressWarnings("deprecation") 6 public static void main(String[] args) { 7 System.runFinalizersOnExit(true); 8 9 10 // 检查该类上是否有@ItcastAnnotation 注解: 11 if (AnnotationTest.class.isAnnotationPresent(ItcastAnnotation.class)) { 12 ItcastAnnotation annotation = (ItcastAnnotation)AnnotationTest.class.getAnnotation(ItcastAnnotation.class) ; 13 System.out.println(annotation.color()); 14 System.out.println(annotation.arrayAttr()); 15 System.out.println(annotation.value()); 16 System.out.println(annotation.lamp().nextLamp()); 17 } 18 19 } 20 21 }
7、泛型
详细请看:http://www.cnblogs.com/jbelial/archive/2013/03/30/2990935.html
总结:泛型只是在编译阶段帮助编写程序时避免存储集合时类型出错。在运行阶段则没有泛型:
1 public class GenericTest { 2 public static void main(String[] args) throws IllegalArgumentException, SecurityException, IllegalAccessException, InvocationTargetException, NoSuchMethodException { 3 List<Integer> arr = new ArrayList<Integer>() ; 4 //通过反射来验证:泛型只是在编译阶段帮助编写程序时避免存储集合时类型出错。 5 arr.getClass().getMethod("add", Object.class).invoke(arr, "abc") ; 6 System.out.println(arr.get(0)); 7 } 8 }
尽管我存入的是一个"abc"字符串对象,但是还是可以打印出来。
三、反射:
反射不是jdk1.5的新特性。反射就是把Java 类中的各种成分映射成相应的Java类。例如。一个Java类中用一个Class类的对象来表示一个类中的组成部分:成员变量,方法,构造方法,包等信息也
用一个个Java类来表示,就像汽车是一个类,汽车中的发动机,变速箱等等也是一个个的类。表示Java类的Class类显然要提供一系列的方法,来获得其中的变量,方法,构造方法,修饰符,包等信息。
这写信息就是用相应的实例对象来表示,它们是Field、Method、Contructor、Package等。
1、反射的基石->Class 类。
注意:class 表示类,而Class则是一个类名。Java程序中的各个Java类属于同一类事物,描述这类事物的Java类名就是Class。
Class类描述了哪些方面的信息呢?类的名字,类的访问属性,类所属于的包名,字段名称的列表,方法名称的列表,等等。
问:如Person 类代表人,那么它的实例对象就是张三,李四这样一个个具体的人,Class类代表Java类,他的各个实例对象又分别对应什么呢?
Class的各个实例对应在内存中的字节码,例如Person 类的字节码,ArrayList 类的字节码等等。一个类被加载器加载到内存中,占用一片存储弓箭,这个空间里面的内容就是类的字节码,不同的类的字节码是不同的,所以它们在内存中的内容是不同的,这一个个的空间可分别用一个个的对象来表示,这些对象显然具有相同的类型
问:如果得到各个字节码对应的实例对象:
类名.class , 例如System.class
对象.getClass() , 例如 new Data().getClass() ;
Class.forName("类名")
九个预定义Class对象:boolean
、byte
、char
、short
、int
、long
、float
和 double
和关键字 void
也表示为 Class
对象。
> public boolean isPrimitive()
:判定指定的 Class
对象是否表示一个基本类型。有九种预定义的 Class
对象,表示八个基本类型和 void。这些类对象由 Java 虚拟机创建,与其表示的基本类型同名,即 boolean
、byte
、char
、short
、int
、long
、float
和 double
。
> Int.Class == Integer.TYPE
> 数组类型的Class 实例对象:Class.isArray()
总结:只要在源程序中出现的类型,都有各自的Class实例对象。
1 public class ReflectTest { 2 public static void main(String[] args) throws ClassNotFoundException { 3 String str = "abc" ; 4 Class cls1 = str.getClass() ; 5 Class cls2 = String.class ; 6 Class cls3 = Class.forName("java.lang.String") ; 7 //无论用什么方法获得字节码,都是同一个字节码。 8 System.out.println(cls1 == cls2); 9 System.out.println(cls1 == cls3); 10 } 11 }
2、构造方法的反射应用
Constructor 类:代表某个类中的一个构造方法。
1 public class ReflectTest { 2 public static void main(String[] args) throws Exception { 3 // 得到某一个类的所有构造方法: 4 Constructor<String>[] conAll = 5 (Constructor<String>[]) String.class.getConstructors() ; 6 for(Constructor<String> con : conAll) { 7 System.out.println(con); 8 } 9 // 得到某个类的指定构造方法 10 Constructor<String> con = 11 (Constructor<String>) Class.forName("java.lang.String").getConstructor(StringBuffer.class) ; 12 System.out.println("con:"+con); 13 14 // 创建实例对象 15 // 通常方法: 16 String str = new String(new StringBuffer("abcd")) ; 17 // 反射方法: 18 String strCon = con.newInstance(new StringBuffer("abcd")) ; 19 20 } 21 }
打印结果:
public java.lang.String() public java.lang.String(java.lang.String) public java.lang.String(char[]) public java.lang.String(char[],int,int) public java.lang.String(int[],int,int) public java.lang.String(byte[],int,int,int) public java.lang.String(byte[],int) public java.lang.String(java.lang.StringBuilder) public java.lang.String(byte[],int,int,java.lang.String) throws java.io.UnsupportedEncodingException public java.lang.String(byte[],int,int,java.nio.charset.Charset) public java.lang.String(byte[],java.lang.String) throws java.io.UnsupportedEncodingException public java.lang.String(byte[],java.nio.charset.Charset) public java.lang.String(byte[],int,int) public java.lang.String(byte[]) public java.lang.String(java.lang.StringBuffer) con:public java.lang.String(java.lang.StringBuffer)
总结:用Constructor 类的到构造方法并创建实体对象分为两步:
1、获得构造方法:getConstructors() 或者 getConstructor 方法。
2、newInstance 方法 创建实例对象。
Class.newInstance() 方法创建实例对象:
> String obj =(String ) String.class.newInstance() ;
该方法内部先得到默认的构造方法,然后用该构造方法创建实例对象。
3、成员变量的反射
Field 类代表某个类中的成员变量。
1 public class ReflectPoint { 2 private int x ; 3 public int y ; 4 public String str ; 5 public String str1 ; 6 public ReflectPoint(int x, int y) { 7 super(); 8 this.x = x; 9 this.y = y; 10 } 11 12 public static void ReflectText() throws Exception{ 13 ReflectPoint point_1 = new ReflectPoint(3,4); 14 ReflectPoint point_2 = new ReflectPoint(5,10); 15 // 得到某个成员变量的字节码 16 // getField()只能获取可见的属性,private 修饰的属性则不能被获取。 17 Field fieldY = point_1.getClass().getField("y") ; 18 // fieldY 不是point_1对象身上y属性的值。而是一个对象,该对象存储了ReflectPoint类的y属性的所有值和它对应的对象。 20 System.out.println(fieldY.get(point_2)); 21 System.out.println(fieldY); 22 23 // 对于用private修饰的属性,则可以用getDeclaredField() 方法获取。 24 // 然后再调用 setAccessible(true) ; 设置为可取。 这样的方法称为暴力反射。 25 Field fieldX = ReflectPoint.class.getDeclaredField("x") ; 26 fieldX.setAccessible(true) ; 27 System.out.println(fieldX.get(point_2)); 28 } 29 public static void main(String[] args) throws Exception { 30 ReflectText() ; 31 } 32 }
对于Field 类的对象,我的理解是:存储方式就像用一个Map集合存储:对象:对应属性值。 如 point_1:4 , point_2:10 ;
练习:将任意一个对象中的所有String 类型的成员变量所对应的字符串内容中的"b"改成"a" 。
思路:1、获取对象中的所有属性,存为一个Field 数组。
2、遍历每个Field对象存储的类的属性类型。
3、如果是String 类型那么则把他的值中的'b'改为'a'。
4、通过set 方法,修改源对象中的值。
1 public class ReflectPoint { 2 private int x ; 3 public int y ; 4 public String str ; 5 public String str1 ; 6 public ReflectPoint(String str, String str1) { 7 super(); 8 this.str = str; 9 this.str1 = str1; 10 } 11 public ReflectPoint(int x, int y) { 12 super(); 13 this.x = x; 14 this.y = y; 15 } 16 17 public static void main(String[] args) throws Exception { 18 ReflectPoint rp = new ReflectPoint("ball","boy") ; 19 System.out.println(rp); 20 changeStringValue( rp) ; 21 System.out.println(rp); 22 } 23 24 // 练习:将任意一个对象中的所有String 类型的成员变量所对应的字符串内容中的"b"改成"a" , 25 public static void changeStringValue(Object obj) throws Exception { 26 Field[] fields = obj.getClass().getFields() ; 27 for (Field field : fields) { 28 // if(field.getType().equals(String.class)){ 29 if(field.getType() == String.class) { 30 String oldString = (String)field.get(obj) ; 31 String newString = oldString.replace('b', 'a') ; 32 // 修改保存 33 field.set(obj, newString) ; 34 } 35 } 36 } 37 public String toString() { 38 return str1+ ":" + str ; 39 } 40 }
4、成员方法的反射:
Method 类 代表某个类中的方法成员。得到类中的某一个方法。
Method charAt = String.class.getMethod("charAt",int.class )
调用方法:
>通常方法:System.out.println(str.CharAt(1)) ;
>反射方法: System.out.println(charAt.invoke(str,1)) ;
1 public class ReflectMethod { 2 public static void main(String[] args) throws Exception { 3 Method charAt = String.class.getMethod("charAt", int.class) ; 4 // 反射方法:方法对象调用对象的方法。 5 System.out.println(charAt.invoke("abcd", 1)) ; 6 } 7 }
如果传递给Method 对象的invoke() 方法的第一个参数为null,这有着什么样的意义呢?说明该Method对象对应的是一个静态方法。
1.4和1.5的invoke方法的区别:
1.5:public Object invoke(Object obj , Object... args) ;
1.4: public Object invoke(Object obj , Object[] args) ;
按照jdk1.4的语法,需要将一个数组作为参数传递给invoke 方法时,数组中的每一个元素分别对应被调用方法的一个参数,所以调用chatAt 方法的代码也可以用jdk1.4改写为charAt.invoke("str", new Object[]{1}) 形式。
问:为什么要用反射方式去调用类中的方法?
在写程序的时候不知道具体要执行哪个类,然后可以调用反射方式调用。
练习:写一个程序,这个程序能够根据用户提供的类名,去执行该类中的test.方法。
1 public class MethodTest { 2 public static void main(String[] args) throws Exception { 3 // 当不知道要调用哪个类,但是类中的方法则是知道的。则可以用反射 4 // 输入要调用的类: 5 String runClass = args[0] ; 6 Method method = Class.forName(runClass).getMethod("test") ; 7 method.invoke(null) ; 8 } 9 } 10 class Test_1{ 11 public static void test() { 12 System.out.println("我是1。"); 13 } 14 } 15 class Test_2{ 16 public static void test(){ 17 System.out.println("我是2。"); 18 } 19 } 20 class Test_3{ 21 public static void test(){ 22 System.out.println("我是3。"); 23 } 24 }
根据用户输入不同的类,而调用不同的test方法。
总结:在启动Java程序的main方法的参数是一个字符串数组,即public static void main(String[] args),通过反射方式来调用这个main时,如何为invoke 方法传递参数呢?
按jdk1.5的语法,整个数组是一个参数,而按照jdk1.4的语法,数组中的每个元素对应一个参数,当把一个字符数组作为参数传递给invoke方法时,jdk1.5会兼容jdk1.4的语法,会按照1.4的语法进行处理,即把数组打散成为若干个单独的参数,所以在给main方法传递参数时,不能使用如下代码:
String runClass = args[0] ; Method method = Class.forName(runClass).getMethod("main") ; method.invoke(null,new String[]{"xxx"....}) ;
解决方案:
method.invoke(null,(Object)new String[]{"xxx"....}) ; method.invoke(null,new Object(new String[]{"xxx"....})) ;
5、数组反射应用
> 具有相同维数和元素类型的数组属于同一个类型,即具有相同的Class实例对象。
1 public class ReflectArray { 2 public static void main(String[] args) { 3 int[] arrInt1 = new int[3] ; 4 int[] arrInt2 = new int[4] ; 5 int[][] arrInt3 = new int[3][4] ; 6 String[] arrString = new String[4] ; 7 System.out.println(arrInt1.getClass() == arrInt2.getClass()); 8 System.out.println(arrInt1.getClass().equals(arrInt3.getClass())); 9 // Incompatible operand types Class<capture#5-of ? extends int[]> and Class<capture#6-of ? extends String[]> 10 // System.out.println(arrInt2.getClass() == arrString.getClass()); 11 System.out.println(arrInt2.getClass().equals(arrString.getClass())) ; 12 13 } 14 }
在arrInt1.getClass() == arrInt3.getClass() 时, 编译便出现了Incompatible operand types Class<capture#5-of ? extends int[]> and Class<capture#6-of ? extends String[]> 错误。
1 // 以 String 的形式返回此 Class 对象所表示的实体(类、接口、数组类、基本类型或 void)名称。 2 System.out.println(arrInt1.getClass().getName());
打印结果为:[I
如果此类对象表示一个数组类,则名字的内部形式为:表示该数组嵌套深度的一个或多个 '[' 字符加元素类型名。元素类型名的编码如下:
Element Type Encoding
boolean Z
byte B
char C
class or interface Lclassname;
double D
float F
int I
long J
short S
> 代表数组的Class实例对象的getSuperClass() 方法返回的父类为Object 类对应的Class 。
> 基本类型的一维数组可以被当作Object 类型使用,不能当作Object[] 类型使用;非基本类型的一维数组既可以当作Object 类型使用,也可以当作Object[]类型使用。
> Array.asList() 方法处理int[] 和 String[] 时的差异。
1 int[] arrInt4 = new int[]{1,2,3} ; 2 String[] arrString2 = new String[]{"a","b","c"}; 3 System.out.println(Arrays.asList(arrInt4)); 4 System.out.println(Arrays.asList(arrString2));
打印结果:
[[I@dc8569]
[a, b, c]
因为,List把整个Int[] 数组当作一个对象存入List集合中,而String[] 中的每一个元素被单独当作元素存入集合。
> Array 工具类用于完成对数组的反射操作。(java.lang.reflect.Array)
写一个方法,作用用于打印传递给它的对象。如果是数组,则打印数组,否则直接打印。
1 public static void printObject(Object obj) { 2 Class cla = obj.getClass() ; 3 if (cla.isArray()) { 4 int len = Array.getLength(obj) ; 5 for (int i = 0 ; i <len ; i ++) { 6 System.out.println(Array.get(obj, i)); 7 } 8 } 9 else { 10 System.out.println(obj); 11 } 12 }
用数组的反射则可以很方便的解决。
四、反射的作用:实现框架功能
写一个小框架,该框架的作用则是根据用户的需求设置不同的集合类型如ArrayList、HashSet等,存入数据。
通过配置文件config.properties 来完成集合的选择:
1 public class RefectTest2 { 2 public static void main(String[] args) throws Exception { 3 InputStream is = new FileInputStream("config.properties") ; 4 Properties prop = new Properties() ; 5 prop.load(is) ; 6 // 关闭is关联的物理资源。 7 is.close() ; 8 String className = prop.getProperty("className") ; 9 10 // 通过反射创建className实体 11 Collection collection = (Collection)Class.forName(className).newInstance() ; 12 13 collection.add("adc") ; 14 collection.add("ak") ; 15 collection.add("wf") ; 16 collection.add("grga") ; 17 collection.add("gk") ; 18 System.out.println(collection.size()); 19 } 20 }