黑马程序员_java基础加强(一) JDK1.5新特性,_javajdk1.5
黑马程序员_java基础加强(一) JDK1.5新特性,_javajdk1.5
------ android培训、java培训、java学习型技术博客、期待与您交流! ----------
JDK1.5新特性
1 静态导入
1). 静态导入基本概念
静态导入的应用背景以及使用
[1]. 采用import关键字导入的是某个包下面的某个类, 要使用Math下的静态方法max(),必须使用“类名.静态成员方法名”的格式
[2]. 为了简化书写,JDK5引入了静态导入。import static两个关键字一起使用,然后在文件中直接采用静态方法名来访问某个类
下的静态成员从而省去“类名.”这个前缀
import static java.lang.Math.*;
public class StaticImport {
public static void main(String[] args) {
System.out.println(Math.max(6, 3));//类名.静态成员方法名
System.out.println(abs(3 -6));//采用静态导入,直接采用静态方法名进行访问
}
}
2 可变参数
1). 可变参数数组的特点以及使用
(1). 可变参数数组的表现形式:数据类型… 参数名
e,g, private static int add(int ... args)
(2). 使用可变参数的实质
调用含有可变参数的方法时候,javac为可变参数部分隐含新建一个数组,所以使用可变参数的实质是把需要的参数打包成一个数组再调用以数组作为参数的方法。
(3). 可变参数数组的特点
[1]. 只能出现在参数列表的最后
[2]. ... 位于参数类型和参数名之间,前后有无空格都可以
[3]. 依据可变参数使用的实质,可以在可变参数所在的方法中以数组的使用形式来操作这个可变参数***
【注意】可变参数的定义式:数据类型…参数名 其中…之前的数据类型一定是被打包成数组的每个元素的类型,而…后面的参数名实际上打包成数组的集合名字!!!是一种总分的关系!
public static void main(String[] args) {
System.out.println(add(2,3));
System.out.println(add(2,3,5));
}
public static int add(int x, int ... args) {
int sum = x;
for (int arg : args) {
sum += arg;
}
return sum;
}
3 享元设计模式思想
1). JDK5出现的现象
Integer i1 =137;//自动装箱成整形对象
Integer i2 =137;//自动装箱成整形对象
System.out.println( i1==i2);//打印出false
i1 =13;
i2 =13;
System.out.println( i1==i2); //打印出true
【**小知识点**】:当一个整数大小在一个字节B(-128~ +127)的取值之间的时候,当被自动包装为相应的对象之后,JVM就会将这些对应的基本数据类型值很小的基本数据类型对应的对象存入缓存池中,以便程序的其他部分复用这个对象。因为Java认为值很小的对象使用频率很高。
【常常使用的东西,存放到公共空间,大家分享,节省内存】
------享元设计模式思想的体现
2)分析上面的代码
Integer i1 =137;//此时相当于 Integer i1 =new Integer(137);
Integer i2 =137;//此时相当于 Integer i2 =new Integer(137);
由于i1和i2指向了两个new出来的堆内存的对象,所以两者的内存地址一定不同,所以 ==判断为false
System.out.println( i1==i2);//打印出false
i1 =13; //13在[-128, +127]之间,并且
第一次出现,所以相当于i1 =new Integer(13);此时JVM就会将这堆内存的对象存放到缓存池中
i2 =13; 这个时候,13再一次出现,JVM此时去缓存池中找有没有值为13的整形对象。发现有,就把这个i2指向这个堆内存的对象。
这时候,i2和i1指向了同一个地址的对象,所以 ==判断结果为true。System.out.println( i1==i2); //打印出true
4 增强for循环(foreach)
语法:for(type 变量名:容器名) {...}
注意:迭代变量必须在()中定义,容器可以是数组或实现Iterable接口的集合类
public static int add(int x,int... args) {
int sum = x;
for(int arg:args) {
sum += arg;
}
return sum;
}
5 枚举(enum)
枚举相当于一个类,其中也可以定义构造方法、成员变量、普通方法和抽象方法。
枚举元素必须位于枚举体中的最开始部分,枚举元素列表后面要有分号与其它成员分离。把枚举中的成员方法
和变量等放在枚举列表前面,编译器会报告错误。
为什么要有枚举?
枚举是为了让某个类型变量的取值,只能是该类型若干个固定值中的一个,否则编译器就报错。枚举可以让编译器
在编译时,就可以控制源程序中的非法值,普通变量的方式在开发阶段无法实现这一目标。
普通类模拟枚举(WeekDay):
* 私有的构造方法
* 每个元素分别用一个公有的静态成员变量表示
* 可以有若干个公有方法或抽象方法,例如要提供nextDay()必须是抽象的,采用抽象方法定义nextDay()
将大量的if else语句转移成了一个个独立的类。
public /*abstract*/ class WeekDay {
private WeekDay() {}; //构造方法私有,
/*
public final static WeekDay SUN = new WeekDay() { //常量是该抽象类匿名子类的实例对象
public WeekDay nextDay() { return MON;} //子类实现父类的抽象方法,简化if else
};
public final static WeekDay MON = new WeekDay() {
public WeekDay nextDay() { return SUN;}
};
public abstract WeekDay nextDay() {}; //父类的方法抽象
*/
public final static WeekDay SUN = new WeekDay();//定义全局变量,是一个对象
public final static WeekDay MON = new WeekDay();
public WeekDay nextDay() {
if(this == SUN)
retunr MON;
return SUN;
}
public String toString() {
return this == SUN ? "SUN" : MON;
}
}
public class EnumDemo {
public static void mian(String[] args) {
/*WeekDay weekday = WeekDay.SUN; //只能方法问WeekDay类型的全局常量
WeekDay weekday = WeekDay.MON;
System.out.println(weekday.nextDay());*/
WeekDay weekday2 = WeekDay.FRI;
System.out.println(weekday2); //打印变量,自动调用toSting()
System.out.println(weekday2.name()); //输出变量的名称,name()
System.out.println(weekday2.ordinal()); //输出常量在第几个位置,ordinal()
System.out.println(WeekDay2.valueOf(“SUN”));//把字符串变成对应的枚举元素,static valueOf()
System.out.println(WeekDay2.values()); //得到一个所有枚举元素组成的数组
}
public enum WeekDay { //定义枚举类
SUN,MON,TUE,WED,THU,FRI,SAT;//元素列表都是全局常量,也是对象
}
带构造方法的枚举:
构造方法必须是私有的(默认)
如果有多个构造方法,在枚举元素后面添加括号(parameter),指定构造方法
枚举类一创建对象,静态变量(枚举元素)就初始化,MON和MON()都是调用默认无参构造方法。
private WeekDay() {System.out.println("无参构造器");} //SUN、SUN()都可以调用默认无参构造器
private WeekDay(int day) {System.out.println("有参构造器");}
//SUN(1),指定参数列表,调用有参构造器
带抽象方法的枚举:
定义枚举TrafficLamp
实现普通的next()方法
实现抽象的next()方法,每个元素分别是由枚举类的子来生成的实力对象,这些类采用类似内部类的方式定义。
增加表示时间的构造方法。
public enum TrafficLamp {
RED(30) { //RED{}是该类的匿名子类实例对象,子类是一个匿名内部类,重写父类中的抽象方法
//RED(30)是调用父类有参构造器
public enum TrafficLamp {
RED(30) { //RED{}是该类的匿名子类实例对象,子类是一个匿名内部类,重写父类中的抽象方法
//RED(30)是调用父类有参构造器
public TrafficLamp nextLamp() {return GREEN;}
}, //常量在class文件中显示为EnumDemo$TrafficLamp$1、$2、$3
GREEN(45) {
public TrafficLamp nextLamp() {return YELLOW;}
},
YELLOW(5) {
public TrafficLamp nextLamp() {return RED;}
};
public abstract TrafficLamp nextLamp();
private int time; //每一个灯都有时间time,
private TrafficLamp(time) {this.time = time;}//通过该类构造函数对time进行初始化
}
枚举:
内部类提高了编程的思想
类的方法可以返回本类类型
类中可以定义静态常量,常量指向是本类子类的实例对象
枚举只有一个成员时,可以作为一种单例的实现方式。
public enum SingletonPattern {
private SingletonPattern() {};
private final static SingletonPattern sp = new SingletonPattern();
public static SingletonPattern getInstance() {return sp;};
/*
private SingletonPattern() {};//单例用枚举来实现,无参构造器默认私有
public fianl static SingletonPattern sp = new SingletonPattern() {
public SingltonPattern getInstance() {return sp;}
};
public abstract SingletonPattern getInstance();
*/
}
6 反射
反射的基石-->Class类:
java程序中的各个java类都属于同一类事物,描述这类事物的java类名就是Class。Class类描述了类的名字,
类的访问属性,类所属的包名,字段名称的列表,方法名称的列表等等。
Class类代表java类,它的实例对象分别对应各个类在内存中的字节码。
一个类被类加载器加载到内存中,占用一片存储空间,这个空间里面的内容就是字节码。不同类的字节码是不同的,
所以它们在内存中的内容也是不同的。这一个个的内存空间分别用一个个的对象来表示,这些对象显然具有相同的
类型,这个类型就是Class类型。通过反射就可以操作Class类。
反射就是把java类中的各种成分映射成相应的java类,把内存中加载的“.class文件”封装成对象(Class),从而
使用其中的构造函数(Constructor)、字段(Field,成员变量)以及方法(Method)。在程序的运行过程中可以
通过一个名字找到指定的类,创建该类对象,反射jdk1.2就出现了,但会导致程序性能严重下降。
Class:获取Class对象:代表内存中的字节码文件(.class文件,也就是类)
类名.class:直接获取指定类的Class对象,内存中不存在则从硬盘加载。如:System.class()
对象.getClass():获取指定对象的所属的Class对象(字节码文件)如:new Date().getClass()
Class.forName(String classname):根据类名获取Class对象,jvm中没有用类加载器从硬盘加载缓存在jvm中
如:Class.forName("java.lang.util")
String str = "abc";
Class c1 = String.class;
Class c2 = str.getClass();
Class c3 = Class.forName("java.lang.String");
System.out.println(c1 == c2);// true
System.out.println(c1 == c3);// true
System.out.println(c1.isPrimitive()); // false:该方法判断字节码是不是原始类型
System.out.println(int.calss.isPrimitive()); // true
System.out.println(int.class == Integer.class);// fals
System.out.println(int.class == Integer.TYPE); // true
System.out.println(int[].class.isPrimitive()); // false
System.out.println(int[].class.isArray()); // true 数组类型的字节码文件
只要是在源程序中出现的类型都有各自的Class实例对象,例如:int[],void,Integer
包装类内部有常量TYPE来表示基本数据类型的字节码
通过Class创建对象:Class.newInstance(),内部通过无参构造函数创建,没有无参构造函数则无法使用
Constructor:构造方法的反射
Class.getConstructors:获取某个类所有的构造方法字节码文件,返回Constructor类型的数组
Class.getConstructor(Class...) :获取指定的构造函数
String.class.Constructor(Class[] parameterTypes);(jdk5.0之前用数组来表示参数可变)
Constructor.newIntance(Object...) :调用构造函数的方法传入实参建立对象
Field(字段):成员变量的反射
Class.getField(String):根据字段名获取对象指定的字段(Field)(包括继承, 但不包括私有)
Class.getDeclaredField(String):获取所有在当前类中声明的属性(不包括继承, 但包括私有)
Filed.setAccessible(boolean) 设置该属性是否可以访问
Field.set(Object, Object) 设置指定对象的属性为指定的值
Filed.get(Object) 获取指定对象的属性值
Method:成员方法的反射
Class.getMethod(String, Class...) 根据名字和参数类型获取方法(可见的)
Class.getDeclaredMethod(String, Class...) 根据名字和参数类型获取方法(该类中声明的)
Method.setAccessible(boolean) 设置方法是否可以访问
Method.invoke(Object, Object...) 在指定对象上执行方法, 传入若干参数
参数为数组的成员方法的反射(以main函数为例):
String startClassName = args[0];
Method mainMethod = Class.forName(startClassName).getMethod("main",String[].class)
mainMethod.invoke(null,new Object[]{new String[]{"11","22","33"}});
main方法的参数是一个字符串数组,jdk1.5的语法是把整个数组看成一个参数,但是1.4的语法是是把
数组中的每一个元素看成一个参数。为了兼容性考虑,按照1.4的语法把数组打散成若干个独立的参数。
所以在使用invoke()方法时,new String[]{"11","22","3"}作为参数传递就会出现类型不对的问题。
这时可以将它当成数组中的一个元素看待,即new Object[]{new String[]{"11","22","33"}}
另外一种解决方式就是把编译时不要把它当成数组看待,直接在前面加(Object)进行强转
数组的反射:
具有相同纬度和元素类型的数组属于同一个类型,即具有相同的Class对象
代表数组Class实例对象的getSuperclass()方法返回的父类为Object类对应的Class
如: int[] a1 = new int[3];
int[] a2 = new int[2];
int[][] a3 = new int[2][3];
String[] str = new String[3]
System.out.println(a1.getClass() == a2.getClass());//true
System.out.println(a1.getClass() == a3.getClass());//false
System.out.println(a1.getClass() == a4.getClass());//false
System.out.println(a1.getClass().getName()); // [I:维度和数据类型
System.out.println(a1.getClass().getSuperclass().getName()); //java.lang.Object
System.out.println(a4.getClass().getSuperclass().getName()); //java.lang.Object
数组与Object的关系及其反射类型:
基本类型的一维数组尅当作Object类型使用,不能当作Object[]类型使用
非基本类型的一维数组,既可以当作Object类型使用,又可以当作Object[]使用
Object obj1 = a1; //一维数组是Object类型的
Object[] obj2 = a1; //基本数据类型不能转换为Object数组
Object[] obj3 = a3; //a3是包含一个int类型一维数组的数组,这个一维数组是Object类型
Object[] obj4 = ar; //String类型是Object类型
查看数组中的内容:
int[] a1 = new int[]{1,2,3};
String[] str = new String[]{"a","b","c"};
System.out.println(Arrays.asList(a1));// [[I@1cfb549] 转换成集合后,只有一个元素,是原数据对象
System.out.println(Arrays.asList(str)); //[a,b,c],把数组转成集合后才能打印数组中的内容
数组的反射应用
Arrays工具类用于完成对数组的反射操作
Object obj = a1;
Class clazz = obj.getClass(); //获取obj的class文件
if(clazz.isArray) { //如果字节码是一个数组,表示它也是一个数组
int len = Array.getLength(obj); //Array.getLength(obj)获取obj的长度
for(int i = 0;i < len;i++) { //遍历数组
System.out.println(Array.get(obj,i)); //Array.get(obj,i)获取obj对象i角标上的元素
}
} else
System.out.println(obj); //如果不是数组,直接输出obj对象
数组中的元素类型:
不能得到整个数组的类型,但可以得到数组中元素的类型
取出每个元素对象,对各个对象进行判断,因为每个元素的类型都可以不同。例如:
Object[] obj = new Object[]{"a",1};
//没有办法得到整个数组的类型,到底是String还是int分不清楚
//得到具体元素的类型:obj[0].getClass().getName()
hashCode()分析:
//Collection collections = new ArrayList();集合长度为4
Collection collectons = new hashSet();
ReflectPoint pt1 = new ReflectPonint(3,3);
ReflectPoint pt2 = new ReflectPonint(5,5);
ReflectPoint pt3 = new ReflectPonint(3,3);
collections.add(pt1);
collections.add(pt2);
collections.add(pt3);
collections.add(pt1);
System.out.println(collections.size());// 集合长度为3
//如果重写hashCode()和equals()方法,集合的长度就变为2
//equals()方法比较的是内容,pt1和pt2相同,只存一个
//如果没有重写hashCode()方法,集合长度又为3。因为pt1和pt3通过Object的hashCode()有两个不同的哈希值
//hashCode()的作用:
该方法把集合分成若干个存储区域,每个对象通过哈希算法算出一个哈希值,根据算出的值把对象放入对应
的区域,这样查找的性能就提高了。只有底层是哈希结构的集合hashCode()才有价值。
当一个对象被存储进hashSet集合中后,不能修改对象中那些参与计算哈希值的字段(属性)。
例如:pt1.y = 7;
collectons.remove(pt1);
System.out.println(); // 集合的长度仍然是重写两个方法后的长度
//因为属性参与了哈希值的计算,属性改变后哈希值也改变,再删除pt1就找不到对应的哈希值
//这时删除就不成功,会造成内存泄漏,如果反复进行这种操作,积累起来就会内存溢出。
//内存泄漏就是对象没有使用,占用内存空间,没有被释放。
7 框架
反射实现框架功能:
框架与工具类:
框架与工具类有区别,工具类被用户的类调用,而框架则是调用用户提供的类。
框架要解决的核心问题:
简单来讲,框架程序就是要调用以后的类。因为在写程序时,无法知道要被调用的类名。所以在程序中无法直接
new某个类的实例对象,这时通过反射的方式实现。
综合案例:
8 内省(IntroSpector)
用于对JavaBean操作。JavaBean是一个特殊的类。主要用于传递数据信息。这种Java类中的方法主要用于访问私有的字段
且方法名符合某种命名规则。
如果要在两个模块之间传递多个信息,可以将这些信息封装到一个JavaBean中,这种JavaBean的实例对象通常称之为
值对象(ValueObject 简称VO)。这些信息在类中用用私有字段来存储,如果读取或设置这些字段的值,则需要通过一些
相应的方法来访问。
JavaBean的属性根据对外访问属性的方法得来,而不是根据成员变量得来。即去掉set/get后,剩下来的名称就是
JavaBean的属性,把剩余部分的首字母改成小的,但是有条件:
如果第二个字母是小写,首字母改成小写。 getId-->id
如果第二个字母是大写,首字母保持不变,好看。getCPU->CPU
总之,一个类被当作JavaBean使用时,JavaBean的属性是根据方法名推断出来的,它根本看不到java类的成员变量。
一个符合JavaBean特点的类可以当作普通类使用,但把它当作JavaBean使用有以下好处:
在JavaEE开发中,经常要使用到JavaBean。很多环境就要求按JavaBean方式进行操作。
JDK中提供了对JavaBean进行操作的一些API,这套API就成为内省。如果我们自己通过getX方法来访问私有的成员变量
x,难度非常大。用内省这套API操作JavaBean比用普通类的方式更方便。
内省综合案例:
对某个对象的属性进行获取-->用JavaBean的方式:
①建立一个带有set/get方法的类实例对象obj
Class clazz = Object.class;
Object obj = clazz.newInstance();
②String propertyName = "x";//假设属性名字,其实是看不到的
//"x"-->"X"-->"getX"-->MethodGetX-->...用反射反射的方式太麻烦
public static Object getProperty(Object obj,String propertyName) {
//通过这个类的构造函数从obj对象上获取到属性propertyName
③PropertyDescriptor pd = new PropertyDescriptor(propertyName,obj.getClass())
④Method methodGetX = pd.getReadMethod(); //获取对象的getX()方法
⑤Object returnValue = methodGetX.invoke(obj); //调用获取到的方法,就获obj对象的属性值。
}
⑥System.out.println(returnValue);
public static void setProperty(Object obj,String propertyName,Object value) {
⑦Method methodSetX = pd.getWriterMethod(); //获取对象的setX()方法,这时要处理异常
⑧methodSetX.invoke(obj,7); //调用方法,将obj对象的属性值设为7
}
对某个对象的属性复杂获取-->IntroSpector类:
在程序中把一个类当作JavaBean来看,调用IntroSpector.getBeanInfo()方法,得到的BeanInfo类型对象
封装了把这个类当作JavaBean看的结果的信息。
采用遍历BeanInfo的所有属性来查找和设置某个对象的属性
public static Object getProperty(Object obj,String propertyName) {
BeanInfo beanInfo = IntroSpector.getBeanInfo(obj.getClass());//返回obj当作JavaBean看的结果
PropertyDescriptor[] dps = beanInfo.getPropertyDescriptors();//获取属性方法返回一个属性数组
Object returnValue = null; //
for(PropertyDescriptor pd : dps) { //增强for循环遍历属性数组
if(pd.getName().equals(propertyName) //获取到的属性名字和传入的相等
Method methodGetX = pd.getReadMethod(); //获取对象的getX()方法
retVal = methodGetX.invoke(obj); //调用获取到的方法,获取obj对象的属性值。
}
return returnValue;
}
------ android培训、java培训、java学习型技术博客、期待与您交流! ----------
相关文章
- 暂无相关文章
用户点评