目录
1、JDK5特性
1.1、静态导入
1.2 增强for循环
1.3 可变参数
1.4 自动装箱/拆箱
1.4.1 基本数据类型包装类
1.5 枚举类
1.6 泛型
1.6.1 泛型方法
1.6.2 泛型类
1.6.3 泛型接口
1.6.4 泛型通配符
1、JDK5特性
JDK5中新增了很多新的java特性,利用这些新语法可以帮助开发人员编写出更加清晰,安全,高效的代码。
静态导入
自动装箱/拆箱
增强for循环
可变参数
枚举
泛型
1.1、静态导入
JDK 1.5 增加的静态导入语法用于导入类的某个静态属性或方法。使用静态导入可以简化程序对类静态属性和方法的调用。
语法:
import static 包名.类名.静态属性|静态方法|*
例如:
import static java.lang.System.out
import static java.lang.Math.*
import static java.lang.System.out; import static java.lang.Math.*; public class Demo { public static void main(String[] args) { // 普通写法 System.out.println("hello world"); int max = Math.max(100, 200); System.out.println(max); // 静态导入 out.println("hello world"); int max2 = max(100, 200); System.out.println(max2); } } |
1.2 增强for循环
引入增强for循环的原因:在JDK5以前的版本中,遍历数组或集合中的元素,需先获得数组的长度或集合的迭代器,比较麻烦!
因此JDK5中定义了一种新的语法——增强for循环,以简化此类操作。增强for循环只能用在数组、或实现Iterable接口的集合类上
语法格式:
for(变量类型 变量 :需迭代的数组或集合){} |
For each是为了让你的代码变得简捷、和容易维护。
它的速度也普通遍历的速度是一致的。
1.3 可变参数
JDK中具有可变参数的类Arrays.asList()方法。
分别传多个参、传数组,传数组又传参的情况。
注意:传入基本数据类型数组的问题。
从JDK 5开始, Java 允许为方法定义长度可变的参数。
语法:数据类型…变量名。
可变长参数是Object[] 数组。(可变参数里存的是对象数组)
JDK中的典型应用:
Arrays.asList(T…a)是jdk中的典型应用。
需求:对若干个整数进行求和
public static int sum1(int a,int b ) { return a+b; } |
若干个整数求和如何解决?
可以使用数组接收整数。
public static int sum1(int[] numbers) { if (numbers == null) { return 0; } if (numbers.length == 0) { return 0; } int sum = 0; for (int num : numbers) { sum += num; } return sum; } |
可以使用可变参数
public static int sum2(int... numbers) { if (numbers == null) { System.out.println("可变参数的值为null"); return 0; } if (numbers.length == 0) { System.out.println("可变参数的值的长度为0"); return 0; } int sum = 0; for (int num : numbers) { sum += num; } return sum; } |
可变参数的使用
public static void main(String[] args) { // int result = sum1(new int[] { 1, 3, 5, 7, 9 }); // System.out.println(result); // // 使用了可变参数,传一个数组进去 // int result = sum2(new int[] { 1, 3, 5, 7, 9 }); // System.out.println(result); // 使用了可变参数,不必声明数组,简化书写 // int result = sum2(2, 4, 6, 8, 10); // int result = sum2(1); int result = sum2(); System.out.println(result); } |
可变参数的细节 声明: 在一个方法中,最多只能有一个可变参数。 可变参数只能放在参数列表的最后面。 调用: 当使用可变参数时,可以传0或多个参数。 当使用可变参数时,也可以传一个数组进去,就表示多个参数。 使用: 在方法内部使用时,就是在使用一个数组。 当调用时没有传参数时(传了0个),这时在方法内部的参数数组是有值的(不为null),但长度为0. |
1.4 自动装箱/拆箱
自动装箱:指开发人员可以把一个基本数据类型直接赋给对应的包装类。
自动拆箱:指开发人员可以把一个包装类对象直接赋给对应的基本数据类型。
典型应用:
List list = new ArrayList();
list.add(1);
//list.add(new Integer(1));
int i=list.get(0);
//int j = (Integer)list.get(0);
1.4.1 基本数据类型包装类
包装类 基本数据类型
Byte | byte |
Short | short |
Integer | int |
Long | long |
Boolean | boolean |
Float | float |
Double | double |
Character | char |
对象变基本数据类型:拆箱 | 基本数据类型包装为对象:装箱 |
为了使得java的基本类型有更多的功能,java为其所有的基本类型提供了包装类来封装常见的功能。如:最大值、数值转换等。
将基本数据类型封装成对象的好处在于可以在对象中定义更多的功能方法操作该数据
所属的包:java.lang.*
常见应用一:
获取最大最小值MAX_VALUE / MIN_VALUE
整数类型最大值
Integer.MAX_VALUE
System.out.println(Integer.MIN_VALUE);// -2147483648 System.out.println(Integer.MAX_VALUE);// 2147483647 |
应用二:
基本数据类型和字符串之间的转换
例:Integer的parseInt方法,intValue方法
基本数据类型转换成字符串:
1:基本数据类型+””
2:基本数据类型.toString(基本数据类型值);
例如 Integer.toString(34); //将34变成了”34”
基本数据类型转字符串
int i=100; String str=100+""; String string = Integer.toString(100); |
字符串变基本数据类型
基本数据类型 a=基本数据类型包装类.parse基本数据类型(String str);
str="123"; int parseInt = Integer.parseInt(str); System.out.println(parseInt); |
注意:
public static int parseInt(String s) Integer类中的parseInt方法是静态的 参数必须是数字格式 |
Double
str = "3.14"; double parseInt2 = Double.parseDouble(str); System.out.println(parseInt2); boolean b = Boolean.parseBoolean("true"); |
应用三:
进制转换:
十进制转成其他进制.
toBinaryString(int i) toHexString(int i) toOctalString(int i) 那么其他进制转成十进制 parseInt(String radix); parseInt(String s, int radix) |
十进制转其他进制
// 十进制转二进制 String binaryString = Integer.toBinaryString(100); System.out.println(binaryString); // 1100100 // 十进制转十六进制 String hexString = Integer.toHexString(100); System.out.println(hexString); // 64 // 十进制转八进制 String octalString = Integer.toOctalString(100); System.out.println(octalString); // 144 |
其他进制转十进制
// 字符串转对应的进制 int parseInt3 = Integer.parseInt(octalString); System.out.println(parseInt3); // 二进制转十进制 int parseInt4 = Integer.parseInt(binaryString, 2); System.out.println(parseInt4); // 十六进制转十进制 int parseInt5 = Integer.parseInt(hexString, 16); System.out.println(parseInt5); // 八进制转十进制 int parseInt6 = Integer.parseInt(octalString, 8); System.out.println(parseInt6); |
JDK5.0后出现了自动装箱和拆箱
JDK5.0以后,简化了定义方式。
Integer x = new Integer(5);//装箱 int intValue = x.intValue(); //拆箱 // 5.0简化书写 // 自动装箱。new Integer(5); Integer y = 5; // 对象加整数,x 进行了自动拆箱,变成了int 型 和5进行加法运算后再将和进行装箱赋给x。 y = y + 5; // 是通过Integer.intValue() 方法进行拆箱 |
练习:
public static void main(String[] args) { Integer a = 127; Integer b = 127; System.out.println(a == b); Integer c = 128; Integer d = 128; System.out.println(c == d); } |
请问结果?
a==b 为true 因为a和b指向了同一个Integer 对象.
Integer的缓存大小-128 ~127 之间也就是byte的范围。
1.5 枚举类
有时候,变量的取值只是在一个有范围内,例如:服装的尺寸有小,中,大,和超大尺寸.我们可以将这些尺寸定义为定义变量并保存值为 1,2,3,4 或者S M L X ,这样可以解决问题.但是变量很有可能保存了一个错误的值(0 或者m)
一些方法在运行时,它需要的数据不能是任意的,而必须是一定范围内的值,此类问题在JDK5以前采用自定义带有枚举功能的类解决,Java5以后可以直接使用枚举予以解决。
例如: 交通灯(红、黄、绿) 性别(男、女) 星期(星期一、二、三…..)
分数等级(A、B、C、D、E)
JDK 5新增的 enum 关键字用于定义一个枚举类。
枚举的实现
使用enum定义枚举类
在枚举类中定义枚举值(大写)
enum Gender { MALE, FEMALE; } |
使用javap命令
发现其中每一个枚举值都是枚举类的具体实例对象.只不过是静态常量.
枚举类具有如下特性:
枚举类也是一种特殊形式的Java类。
枚举类中声明的每一个枚举值代表枚举类的一个实例对象。
与java中的普通类一样,在声明枚举类时,也可以声明属性、方法和构造函数.
public class Demo1 { public static void main(String[] args) { Gender male = Gender.MALE; System.out.println(male.getInfo()); } } enum Gender { MALE("男"), FEMALE; // 成员变量 private String info; // 构造函数 private Gender() { } private Gender(String info) { this.info = info; } // 成员方法 public String getInfo() { return info; } } |
枚举类可以声明抽象方法,但是要有具体的枚举值去实现.
public class Demo1 { public static void main(String[] args) { Gender male = Gender.MALE; System.out.println(male.getInfo()); male.speak(); } } enum Gender { MALE("男") { @Override public void speak() { System.out.println("是男人"); } }, FEMALE { @Override public void speak() { System.out.println("是女人"); } }; // 成员变量 private String info; // 构造函数 private Gender() { } private Gender(String info) { this.info = info; } // 成员方法 public String getInfo() { return info; } public abstract void speak(); } |
枚举类也可以实现接口(序列化)、或继承抽象类。
JDK5中扩展了swith语句,它除了可以接收int, byte, char, short外,还可以接收一个枚举类型(enum)。
public class Demo2 { public static void main(String[] args) { WeekDay mon = WeekDay.MON; switch (mon) { case MON: System.out.println("星期一要上班..."); break; case TUE: System.out.println("星期二,继续上班..."); break; } } } enum WeekDay { MON, TUE, WED, THU, FRI, SAT, SUN; } |
若枚举类只有一个枚举值,则可以当作单态设计模式使用。
练习:
请编写一个关于星期几的枚举WeekDay,要求:枚举值:Mon,Tue,Wed,Thu,Fri,Sat,Sun 该枚举要有一个方法,调用该方法返回中文格式的星期。
enum WeekDay { MON { @Override public String getInfo() { return "星期一"; } }, TUE { @Override public String getInfo() { return "星期二"; } }, WED { @Override public String getInfo() { return "星期三"; } }, THU { @Override public String getInfo() { return "星期四"; } }, FRI { @Override public String getInfo() { return "星期五"; } }, SAT { @Override public String getInfo() { return "星期六"; } }, SUN { @Override public String getInfo() { return "星期天"; } }; public abstract String getInfo(); } |
1.6 泛型
1.6.1 泛型方法
需求:写一个函数,调用者传递什么类型的变量,该函数就返回什么类型的变量?
实现一:
由于无法确定具体传递什么类型的数据.那么方法的形参就定义为Object类型.返回值也就是Object类型.但是使用该函数时需要强制类型转换.
private Object getDate(Object obj) { return obj; } |
当不进行强制类型转换能否写出该功能?
目前所学的知识无法解决该问题
就需要使用泛型类解决
使用的泛型的自定义来解决以上问题。
泛型: 就是将类型当作变量处理。规范泛型的定义一般是一个大写的任意字母。
1. 函数上的泛型定义 当函数中使用了一个不明确的数据类型,那么在函数上就可以进行泛型的定义。 public <泛型的声明> 返回值类型 函数名( 泛型 变量名 ){ } |
public static void main(String[] args) { int[] arr = { 1, 2, 3, 4, 5 }; new Demo6().getData(5); } public <T> T getData(T data) { return data; } |
细节:
使用泛型方法前需要进行泛型声明,使用一对尖括号 <泛型>,声明的位置在static后返回值类型前。
当一个类中有多个函数声明了泛型,那么该泛型的声明可以声明在类上。
1.6.2 泛型类
格式
2. 类上的泛型声明 修饰符 class 类名<泛型>{ } |
import java.util.Arrays; public class Demo6<T> { public static void main(String[] args) { // 使用泛型类,创建对象的时候需要指定具体的类型 new Demo6<Integer>().getData(5); } public T getData(T data) { return data; } // 反序任意类型数组 public void reverse(T[] arr) { int start = 0; int end = arr.length - 1; for (int i = 0; i < arr.length; i++) { if (start < end) { T temp = arr[start]; arr[start] = arr[end]; arr[end] = temp; } } } |
在泛型类中定义一个静态方法
public class Demo6<T> { public static void main(String[] args) { System.out.println(getData2(100)); } public T getData(T data) { return data; } //静态方法 public static T getData2(T data) { return data; } } |
注意:静态方法不可以使用类中定义的泛型
因为类中的泛型需要在对象初始化时指定具体的类型,而静态优先于对象存在。那么类中的静态方法就需要单独进行泛型声明,声明泛型一定要写在static后,返回值类型之前
泛型类细节:
1、创建对象的时候要指定泛型的具体类型 2、创建对象时可以不指定泛型的具体类型(和创建集合对象一眼)。默认是Object,例如我们使用集合存储元素的时候没有使用泛型就是那么参数的类型就是Object 3、类上面声明的泛型只能应用于非静态成员函数,如果静态函数需要使用泛型,那么 需要在函数上独立声明。 4、如果建立对象后指定了泛型的具体类型,那么该对象操作方法时,这些方法只能操作一种数据类型。 5、所以既可以在类上的泛型声明,也可以在同时在该类的方法中声明泛型。 |
泛型练习:
定义泛型成员
public class Demo7 { public static void main(String[] args) { Father<String> f = new Father<String>("jack"); System.out.println(f.getT()); Father<Integer> f2 = new Father<Integer>(20); System.out.println(f2.getT()); } } class Father<T> { private T t; public Father() { } public Father(T t) { super(); this.t = t; } public T getT() { return t; } public void setT(T t) { this.t = t; } } |
如果Father类有子类,子类该如何实现
public class Demo7 { public static void main(String[] args) { Father<String> f = new Father<String>("jack"); System.out.println(f.getT()); Father<Integer> f2 = new Father<Integer>(20); System.out.println(f2.getT()); } } class Father<T> { private T t; public Father() { } public Father(T t) { super(); this.t = t; } public T getT() { return t; } public void setT(T t) { this.t = t; } } //子类指定了具体的类型 class Son extends Father<String>{ } //子类也需要使用泛型 class Son3<T> extends Father<T>{ } //错误写法,父类上定义有泛型需要进行处理 class Son2 extends Father<T>{ } |
1.6.3 泛型接口
public class Demo8 { public static void main(String[] args) { MyInter<String> my = new MyInter<String>(); my.print("泛型"); MyInter2 my2 = new MyInter2(); my.print("只能传字符串"); } } interface Inter<T> { void print(T t); } // 实现不知为何类型时可以这样定义 class MyInter<T> implements Inter<T> { public void print(T t) { System.out.println("myprint:" + t); } } //使用接口时明确具体类型。 class MyInter2 implements Inter<String> { @Override public void print(String t) { System.out.println("myprint:" + t); } } |
1.6.4 泛型通配符
需求:
定义一个方法,接收一个集合对象(该集合有泛型),并打印出集合中的所有元素。
例如集合对象如下格式:
Collection<Person> coll = new ArrayList<Person>(); coll.add(new Person("jack", 20)); coll.add(new Person("rose", 18)); Collection<Object> coll2 = new ArrayList<Object>(); coll2.add(new Object()); coll2.add(new Object()); coll2.add(new Object()); Collection<String> coll3 = new ArrayList<String>(); coll3.add("abc"); coll3.add("ddd"); coll3.add("eee"); |
分析,集合对象中的元素的类型是变化的,方法的形参的那么泛型类型就只能定义为Object类型.
import java.util.ArrayList; import java.util.Collection; import java.util.HashSet; import java.util.Iterator; public class Demo9 { public static void main(String[] args) { ArrayList<Object> arr = new ArrayList<Object>(); arr.add(new Object()); arr.add("String"); print(arr);
//将集合的泛型设置类String类型,是Object子类 HashSet<String> hs = new HashSet<String>(); hs.add("hello"); hs.add("jack"); //由于print方法接收的集合进行了元素限定,只接受限定为Object类型的集合,编译不通过 //print(hs); } public static void print(Collection<Object> coll) { Iterator<Object> it = coll.iterator(); while (it.hasNext()) { Object next = it.next(); System.out.println(next); } } } |
但是,由于print方法接收的集合进行了元素限定,只接受限定为Object类型的集合,编译不通过该问题如何解决?
可以把方法的形参的泛型去掉,那么方法中就把集合中的元素当做Object类型处理.
也可以使用使用泛型通配符
public class Demo9 { public static void main(String[] args) { ArrayList<Object> arr = new ArrayList<Object>(); arr.add(new Object()); arr.add("String"); print(arr); // 将集合的泛型设置类String类型,是Object子类 HashSet<String> hs = new HashSet<String>(); hs.add("hello"); hs.add("jack"); // 使用泛型通配符,编译通过。 print(hs); } public static void print(Collection<?> coll) { Iterator<?> it = coll.iterator(); while (it.hasNext()) { Object next = it.next(); System.out.println(next); } } } |
上述就使用了泛型通配符
通配符:? public void show(List<?> list){ } 可以对类型进行限定范围。 ?extends E: 接收E类型或者E的子类型。 ? super E: 接收E类型或者E的父类型。 |
限定泛型通配符的边界
限定通配符的上边界:
extends
接收Number 类型或者Number的子类型
正确:Vector<? extends Number> x = new Vector<Integer>(); 错误:Vector<? extends Number> x = new Vector<String>(); |
限定通配符的下边界
super
接收Integer 或者Integer的父类型
正确:Vector<? super Integer> x = new Vector<Number>(); 错误:Vector<? super Integer> x = new Vector<Byte>(); |
总结:
JDK5中的泛型允许程序员在编写集合代码时,就限制集合的处理类型,从而把原来程序运行时可能发生问题,转变为编译时的问题,以此提高程序的可读性和稳定
注意:泛型是提供给javac编译器使用的,它用于限定集合的输入类型,让编译器在源代码级别上,即挡住向集合中插入非法数据。但编译器编译完带有泛形的java程序后,生成的class文件中将不再带有泛形信息,以此使程序运行效率不受到影响,这个过程称之为“擦除”。
泛型的基本术语,以ArrayList<E>为例:<>念着typeof
ArrayList<E>中的E称为类型参数变量
ArrayList<Integer>中的Integer称为实际类型参数
整个称为ArrayList<E>泛型类型
整个ArrayList<Integer>称为参数化的类型ParameterizedType