day 03 jdk5.0新特性

泛型 --- 通用类型
由来 : java语言 开发者 一批C++ 工程师 ,在c++ 语法中 模板技术 ----- java泛型由来
应用 : 1、类型安全检查 2、编写通用java程序(java框架)

JDK5 之前集合对象使用问题: 1、向集合添加任何类型对象 2、从集合取出对象时,数据类型丢失,使用与类型相关方法,强制类型转换
* 程序存在安全隐患

泛型语法: List<泛型类型> 规定 List集合中元素类型 ,取出集合中元素时,获得具体数据类型元素(不需要进行类型强制转换 )
* 泛型技术 只是 编译器阶段技术,为javac命令 起到类型安全检查作用 ,生成.class文件后,泛型信息将会被 擦除

List<String> ---- 参数化类型

泛型技术 对象 List 、Set 、Map中元素 进行类型安全约束
* 掌握 遍历 使用 类型安全 List 、Set 、Map

使用泛型的对象进行类型转换时,等号两端对象使用泛型类型 必须一致!

使用泛型编写一些通用java程序 : 结合反射技术一起使用 ----- 自定义泛型
1、定义泛型方法,必须在方法的返回值之前进行 泛型类型声明 <泛型类型>
* 编写一个通过数组交换元素方法
* 编写一个通过数组倒序方法
public <T> void reverse(T[] arr) {
/*
* 只需要遍历数组前一半元素,和后一半元素 对应元素 交换位置
*/
for (int i = 0; i < arr.length / 2; i++) {
// String first = arr[i];
// String second = arr[arr.length - 1 - i];
T temp = arr[i];
arr[i] = arr[arr.length - 1 - i];
arr[arr.length - 1 - i] = temp;
}
}

2、在类名后声明类的泛型,当类的泛型使用后,该类中所有方法都可以直接使用泛型 ------ 在类名后 <泛型类型> ---- 不对static方法生效
* 对应泛型类型参数起名 T E K V ---- 泛型类型可以以任意大写字母命名,建议你使用有意义的字母
T Template E Element K key V value
public class ArraysUtils<A> { // 类的泛型
// 将数组倒序
public void reverse(A[] arr) {
/*
* 只需要遍历数组前一半元素,和后一半元素 对应元素 交换位置
*/
for (int i = 0; i < arr.length / 2; i++) {
// String first = arr[i];
// String second = arr[arr.length - 1 - i];
A temp = arr[i];
arr[i] = arr[arr.length - 1 - i];
arr[arr.length - 1 - i] = temp;
}
}

public void changePosition(A[] arr, int index1, int index2) {
A temp = arr[index1];
arr[index1] = arr[index2];
arr[index2] = temp;
}
}

泛型通配符 ---- > ?
泛型 : 任意类型
?:任意泛型类型

通过上下边界,限制通配符类型范围:
? extends Number ----> Number任意子类型 (包含Number)
? super String ---- > String任意父类型 (包含String)
* 上下边界不能同时使用
? extends Object super Integer ----- 没有这么写的

上下边界应用:
范例一
Set 中 addAll(Collection<? extends E> c) ----- 将目标集合c的内容添加到当前set ,? extends E 目标集合是E的子类型
Set<Number> set = new HashSet<Number>();
List<Integer> list = new ArrayList<Integer>();
set.addAll(list); // list 中 Integer 自动转换为 Number

范例二
TreeSet(Comparator<? super E> comparator)
Set<Apple> set = new TreeSet<Apple>(); // 默认需要苹果比较器排序

class FruitComparator implements Comparator<Fruit> {}
Set<Apple> set = new TreeSet<Apple>(new FruitComparator()); // 需要Apple比较器 ,传入 Fruit比较器

小结 泛型
1、 泛型用来在编译阶段 对集合对象进行类型安全检查 ---- 遍历 类型安全List Set Map
2、 泛型技术 结合 反射 编写通用java程序 ---- 定义通过数组交换位置方法、倒序方法 --- 抽取泛型类中
3、 泛型的通配符 和 上下边界 --- 在javaAPI中有很多应用,了解即可

枚举 ----- 用来对一定范围内的值 进行取值
枚举语法 : enum 枚举类名称 { 枚举对象实例,枚举对象实例... ; }
* 可读性良好、阻止非法数据
public class EmployeeTest {

}

class Employee { // 员工类
// 在员工类 内部 定义一个角色属性 (公司内角色只有三种 BOSS MANAGER WORKER)
// 定义员工角色 在三个值当中取一个
private String role1;

private int role2; // 1 BOSS 2 MANAGE 3 WORKER

private Role3 role3; // 在 JDK5 之前 没有枚举,通过自定义类 实现枚举功能

private Role4 role4; // 在 JDK5 之后 引入枚举,使用枚举表示多个角色

public static void main(String[] args) {
Employee employee = new Employee();
employee.role1 = "BOSS";
employee.role1 = "MANAGER";
// 因为角色是一个String
employee.role1 = "MANGAER";// 如果经理字符串拼错了,程序出问题

// -------------------------------------------------------------

employee.role2 = 1; // 可读性太差
employee.role2 = 4; // 非法数据

// ----------------------------------------------------------------
// 定义Role2 类,定义 一组 int 常量,用来表示角色
employee.role2 = Role2.BOSS;// 可读性好多了
employee.role2 = -1; // 非法数据

// --------------------------------------------------------------
// 通过自定义Role3 实现 枚举功能
employee.role3 = Role3.BOSS;
// employee.role3 = new Role3(); // 数据 非法

// 使用枚举之后
employee.role4 = Role4.MANAGER; // 可读性良好,阻止非法数据
// employee.role4 = new Role4(); // 私有构造方法

}
}

class Role2 {
public static final int BOSS = 1;
public static final int MANAGER = 2;
public static final int WORKER = 3;
}

class Role3 { // 枚举功能 类
public static final Role3 BOSS = new Role3();
public static final Role3 MANAGER = new Role3();
public static final Role3 WORKER = new Role3();

private Role3() {
}
}

enum Role4 { // JDK5 以后引用枚举技术 简化对象创建 ---- 功能等价于 Role3
BOSS, MANAGER, WORKER;
}


案例:员工和角色案例 ----枚举技术由来
* 应用 :多个固定取值范围中 取一个值 , 三选一效果

Java数据类型有几种:两大种 引用数据类型 原始数据类型
引用数据类型 :interface class 数组 enum
原始数据类型 :8种

每一个枚举值 在编译.class文件后,成为枚举成员对象(常量)

枚举类构造器 必须private 的

JDK5 switch 可以接受五种数据 类型 byte 、short 、int 、char、enum
JDK7 switch 可以接受六种 : 多了一种String

单例设计模式写法
1、私有构造器
2、private static 成员对象
3、public static 获得成员对象方法
* 懒汉式 和 饿汉式
饿汉:在创建对象时 直接进行初始化
懒汉:在获取对象时 进行初始化

* 在枚举实例定义过程中,向枚举构造器传入参数,通过匿名内部类实现枚举中抽象方法

星期输出中文案例:两种写法
public class WeekDayTest {
public static void main(String[] args) {
WeekDay1 day1 = WeekDay1.Fri;
day1.show();

WeekDay2 day2 = WeekDay2.Wed;
day2.show();
}
}

enum WeekDay2 {
Mon {
@Override
public void show() {
System.out.println("星期一");
}
},
Tue {
@Override
public void show() {
System.out.println("星期二");
}
},
Wed {
@Override
public void show() {
System.out.println("星期三");
}
},
Thu {
@Override
public void show() {
System.out.println("星期四");
}
},
Fri {
@Override
public void show() {
System.out.println("星期五");
}
},
Sat {
@Override
public void show() {
System.out.println("星期六");
}
},
Sun {
@Override
public void show() {
System.out.println("星期日");
}
};
public abstract void show();
}

enum WeekDay1 {
Mon, Tue, Wed, Thu, Fri, Sat, Sun;

// 编写方法 show
public void show() {
// 根据枚举对象 名字 返回响应中文星期
if (this.name().equals("Mon")) {
System.out.println("星期一");
} else if (this.name().equals("Tue")) {
System.out.println("星期二");
} else if (this.name().equals("Wed")) {
System.out.println("星期三");
} else if (this.name().equals("Thu")) {
System.out.println("星期四");
} else if (this.name().equals("Fri")) {
System.out.println("星期五");
} else if (this.name().equals("Sat")) {
System.out.println("星期六");
} else if (this.name().equals("Sun")) {
System.out.println("星期日");
}
}
}


枚举 API
从Enum 继承了很多方法 :
1、name 获得对象名称
2、ordinal 获得对象 下标
3、valueOf(Class,String) 将String类型 枚举对象名称 转换对应Class类型枚举对象

在javac执行编译后,在.class文件中生成两个新的方法
4、values 获得所有枚举对象实例数组
5、valueOf(String) 将String类型 枚举名称 --- 转换 枚举对象
public class EnumAPITest {
@Test
// 枚举对象、枚举对象下标、枚举对象名称表示之间的转换
public void demo2() {
// 第一种 已知枚举对象 --- 获得下标和名称
Color blue = Color.BLUE;
// 获得下标
System.out.println(blue.ordinal());
// 获得名称
System.out.println(blue.name());

System.out.println("----------------------------------");
// 第二种 已知枚举对象 下标 --- 获得枚举对象实例 和 名称
int index = 1;
// 获得枚举对象
Color red = Color.values()[index];
// 获得名称
System.out.println(red.name());

System.out.println("---------------------------------");
// 第三种 已知枚举对象名称 ----- 获得枚举对象实录 和 下标
String name = "YELLOW";

// 获得实例
Color c1 = Enum.valueOf(Color.class, name);
Color c2 = Color.valueOf(name);

// 获得下标
System.out.println(c1.ordinal());
System.out.println(c2.ordinal());
}

@Test
public void demo1() {
// 任何 enum 定义 枚举类 都是默认 继承 Enum 类 ,使用Enum 中方法
Color color = Color.RED; // 枚举对象 不能 new 获得,使用已经创建好对象

// name 方法返回 枚举 实例名称
System.out.println(color.name());

// ordinal 方法 返回 枚举对象 下标
System.out.println(color.ordinal());

// valueOf 将 String 类型 枚举对象 名称 ----- 转换为相应枚举对象
String name = "YELLOW";
Color yellow = Enum.valueOf(Color.class, name); // 将 name 转换 成响应枚举对象
System.out.println(yellow.ordinal());

// 使用枚举类 编译后生成两个方法
// values 获得 所有 枚举对象数组
Color[] colors = Color.values();
System.out.println(Arrays.toString(colors));

// 生成valueOf 只接受String 类型枚举名称,将名称转换为当前枚举类对象
String name2 = "BLUE";
Color blue = Color.valueOf(name2); // 将name2 枚举对象名称 转换 Color对象枚举实例
System.out.println(blue.ordinal());
}
}


* 能力: 可以枚举实例、下标、名称直接 互相转换

静态导入 static import 导入 某个类静态成员 (属性和方法),导入静态成员,在类中可以直接使用
* 虽然静态导入 简化编程,可读性变差 ,导入冲突方法,静态导入不可用

掌握JDK5 之前 自动装拆箱 效果代码写法


foreach循环语句 (for/in语句)
引入原因:替换Iterator复杂写法,本质就是Iterator
foreach语句主要应用:
1、遍历数组
2、遍历Collection 集合对象

如果一个对象 使用forin语句中,该对象 必须满足两个条件
1、类 必须实现 Iterable 接口
2、类 实现 iterater 方法
* 编写Car 让 Car对象用于forin 语句

for/in 实现 就是 Iterator !!!!!!!!!

移除练习
”abc”,”bcd”,”asf”,”ceg”,”daf”,”dfs” 移除所有包含a 字符串

使用迭代器和for/in 进行list循环 ,删除元素时 : java.util.ConcurrentModificationException
1、解决方案:使用Iterator自带 remove方法
2、如果只删除一个元素,可以forin语句删除元素后,通过break跳出循环
3、使用解决这类异常线程安全集合 CopyOnWriteArrayList<E>
ublic class ForInTest {
@Test
public void demo6() {
// 使用线程安全集合对象 ,在forin循环中删除
List<String> list = new CopyOnWriteArrayList<String>();
list.add("abc");
list.add("asf");
list.add("bcd");
list.add("daf");
list.add("ceg");
list.add("dfs");

// 移除所有包含a 元素
for (String s : list) {
if (s.contains("a")) {
list.remove(s);
}
}

System.out.println(list);

}

@Test
public void demo5() {
// 如果在 for循环中只删除一个元素
List<String> list = new ArrayList<String>();
list.add("abc");
list.add("asf");
list.add("bcd");
list.add("daf");
list.add("ceg");
list.add("dfs");

// 删除 daf
for (String s : list) {
if (s.equals("daf")) {
list.remove(s);// 删除 ceg
break;
}
}

System.out.println(list);
}

@Test
public void demo4() {
// List 移除练习
// ”abc”,”bcd”,”asf”,”ceg”,”daf”,”dfs”
List<String> list = new ArrayList<String>();
list.add("abc");
list.add("asf");
list.add("bcd");
list.add("daf");
list.add("ceg");
list.add("dfs");
// 遍历集合移除所有包含字母”a” 的字符串
// 遍历List 三种写法: 通过下标、通过Iterator 、通过forin语句

// 通过下标
// for (int i = 0; i < list.size(); i++) {
// String s = list.get(i);
// if (s.contains("a")) {
// // 需要将s 从 list中移除
// list.remove(s);
// // 防止 元素被跳过
// i--;
// }
// }

// 通过for in
for (String s : list) {
if (s.contains("a")) {
// list.remove(s); // 产生并发异常
// 第一种解决方案 --- 使用迭代器自身删除
}
}

// 使用Iterator进行 List遍历
Iterator<String> iterator = list.iterator();
while (iterator.hasNext()) {
String s = iterator.next();
if (s.contains("a")) {
iterator.remove();
}
}

System.out.println(list);
}

@Test
public void demo1() {
List<String> list = new ArrayList<String>();

list.add("abc");
list.add("def");
list.add("qwe");

// JDK5之前 两种遍历方式 :通过下标遍历、通过Iterator
Iterator<String> iterator = list.iterator();
while (iterator.hasNext()) {
System.out.println(iterator.next());
}
System.out.println("--------------------------------");
for (Iterator iterator2 = list.iterator(); iterator2.hasNext();) {
String string = (String) iterator2.next();
System.out.println(string);
}
System.out.println("--------------------------------");

// for in 简化 Iterator --- for in 就是 Iterator
for (String s : list) { // String s 表示 list中每一个字符串
System.out.println(s);
}
}

@Test
public void demo3() {
// Car 用于 forin语句的
Car car = new Car();
for (String name : car) {
System.out.println(name);
}

// 原理
Iterator<String> iterator = car.iterator();
while (iterator.hasNext()) {
System.out.println(iterator.next());
}
}
}

// Car对象 可以用于 for in 语句
class Car implements Iterable<String> {
String[] names = { "保时捷", "宝马", "奥迪", "桑塔纳", "大众" };

@Override
public Iterator<String> iterator() {
// 自定义迭代器
return new MyIterator();
}

class MyIterator implements Iterator<String> {

int index = 0; // 当前遍历数组下标

@Override
public boolean hasNext() {
if (index >= names.length) { // 证明下标无法取得元素
return false;
}
return true;
}

@Override
public String next() {
String name = names[index];
index++;
return name;
}

@Override
public void remove() {
}

}
}


可变参数 ----- 用来编写框架
原理:int... args 相当于 int[] args , 调用可变参数方法时,传入任意个数参数,任意个数参数都会被保存参数数组中
例如:add(int... args)
add(10,20) ----- > add(new int[]{10,20});
add(20,30,40) ----- > add(new int[]{20,30,40});
add() ------- add(new int[] {});

* 可变参数只能处于参数列表的最后, 所以一个方法最多只能有一个长度可变的参数
public class VariablesTest {
@Test
public void demo2() {
// 数据求和
System.out.println(add(10, 20));
System.out.println(add(20, 30, 40));
System.out.println(add()); // 传入一个长度为0的数组 new int[0]

// 可以直接传入一个数组
System.out.println(add(new int[] { 1, 2, 3, 4, 5, 6, 7 }));
}

// 多个int 类型参数求和 ,int... 表示任意个数 int 类型参数
public int add(int... args) { // 多个参数 可变参数必须是最后一个参数
// args 当做一个数组来使用
int sum = 0;
for (int i : args) {
sum += i;
}
return sum;
}

@Test
public void demo1() {
int[] arr = { 1, 2, 3 };
// 使用Arrays.asList 将数组转换 List
List<int[]> list = Arrays.asList(arr);
System.out.println(list.size());

Integer[] arr2 = { 1, 2, 3 };
List<Integer> list2 = Arrays.asList(arr2);
System.out.println(list2.size());

// 注意 -- 同时传递 数组 和整数时,程序会将数组和整数 作为独立的对象使用
List list4 = Arrays.asList(arr2, 10, 20);
System.out.println(list4.size());

List<Integer> list3 = Arrays.asList(10, 20, 30);
System.out.println(list3.size());
// list3 是不是 ArrayList ?? ---- 不是你知道ArrayList , 该ArrayList 不能改变长度
list3.remove(20);
}
}

如果你想编写高效,功能强大java程序 必须使用反射技术 。
* 反射技术应用:编写框架,通用性很强程序

Class
获得Class对象的三种方法
1、已知类 --- 类名.class获得
2、已知对象 ---- 对象.getClass获得
3、完整类名String格式 ---- Class.forName(完整类名)
* 最常用第三种

通过类字节码对象获得构造器、成员变量、方法 ---- 都各有两个方法
1、获得所有 getConstructors()
2、获得指定 getConstructor(Class<?>... parameterTypes) ---- 参数 构造方法 参数类型字节码对象

Constructor ---- 通过Constructor 创建 字节码对应类 对象
* newInstance(Object... initargs)
在使用Constructor,newInstance() 无参数构造器 ------ 等价于

Field
Field[] fields = c.getDeclaredFields(); 获得当前类声明所有成员变量 包括private
Field name = c. getDeclaredField("name"); 获得当前类中指定名称 成员变量 包括private

* 如何操作private 属性 设置 属性可访问 field.setAccessable(true);
通过Field中 get(Object) 获得指定对象属性值 set(Object,value) 设置指定对象属性值

Method
getDeclaredMethods() 获得所有当前类声明方法
getDeclaredMethod(String name, Class<?>... parameterTypes) 获得指定名称方法
通过Method类 提供 invoke(Object obj,Object... args) 执行方法
public class ReflectTest {
@Test
public void demo5() throws Exception {
// 已知String类型完整类名 ----获得字节码对象
String className = "cn.itcast.reflect.Person";
Class c = Class.forName(className);

// 已知Class对象 ,构造实例
Object obj = c.newInstance(); // 调用无参数构造器

// 获得字节码对象 中指定属性 和方法
// 获得name属性
Field f = c.getDeclaredField("name");
// 获得 setName方法
Method setName = c.getDeclaredMethod("setName", String.class);

// 修改属性的值,执行相应方法
f.setAccessible(true);
f.set(obj, "mary");

setName.invoke(obj, "jor");

// 以上代码 等价于下面代码
Person p = new Person();
// p.name = "mary";
p.setName("jor");
}
//反射方法
@Test
public void demo4() throws Exception {
Person p = new Person();
// 调用 p 对象中setName 设置 name的值 mary
// 1 获得字节码对象
Class c = Class.forName("cn.itcast.reflect.Person");
// 2.操作setName 获得setName 对应反射对象 Method对象
// String类型参数 setName方法
Method setName = c.getDeclaredMethod("setName", String.class);
// 调用p 对象中 setName
setName.invoke(p, "mary"); // p.setName("mary");

// 读取name的值 getName方法
Method getName = c.getDeclaredMethod("getName");
Object name = getName.invoke(p); // p.getName()
System.out.println(name);
}
//反射属性
@Test
public void demo3() throws Exception {
Person p = new Person("mary");
System.out.println(p.getName());

// 使用反射 操作 类成员变量 ---- Field类
// 1、必须获得目标类 字节码对象
Class c = Class.forName("cn.itcast.reflect.Person");

// 2、操作成员实例变量 name --- 获得name 代表Field对象
Field[] f1 = c.getFields(); // 获得所有public 成员变量。包括父类继承
System.out.println(f1.length);
Field[] f2 = c.getDeclaredFields(); // 获得当前类 定义所有成员 --- 包括private
System.out.println(f2.length);

// 获得name 成员变量
Field field = c.getDeclaredField("name"); // 当前field是 private
// 设置private 变量可以访问的
field.setAccessible(true);

// 获得 p 对象指定 name 属性值
Object value = field.get(p); // p.getName()
System.out.println(value);

// 修改p 的name 属性 jor
field.set(p, "jor"); // p.setName("jor");

System.out.println(field.get(p));
}
//反射构造器
@Test
public void demo2() throws Exception {
// 获得Person 字节码对象
String className = "cn.itcast.reflect.Person";
Class c = Class.forName(className);// c 就是 Person 字节码对象

// 通过字节码获得一个类构造器
Constructor[] constructors = c.getConstructors(); // 得到所有构造器
System.out.println(constructors.length);

// 指定获得构造器
Constructor constructor1 = c.getConstructor();// 默认构造器

// 获得String 参数构造器
Constructor constructor2 = c.getConstructor(String.class); // 带有String类型参数构造器

Person p = new Person(); // 调用无参数构造器写法
// 使用反射实现相同功能
Object p2 = constructor1.newInstance(); // 调用无参数构造器,构造person对象
Object p2_2 = c.newInstance();// 通过Class对象 newInstance 调用目标类无参数构造器

Person p3 = new Person("abcd"); // 调用String 参数构造器
// 使用反射 调用String参数构造器
Object p4 = constructor2.newInstance("abcd"); // 调用String 类型构造器 效果等价于 p3
// 创建
}
获取字节码对象
@Test
public void demo1() throws ClassNotFoundException {
// 获得Class对象 三种方式
// 第一种 已知类
Class c1 = ReflectTest.class;

// 第二种 已知对象
Object o = new ReflectTest();
Class c2 = o.getClass();

// 第三种 未知类和对象 知道完整类名
String className = "cn.itcast.reflect.ReflectTest";
Class c3 = Class.forName(className);

System.out.println(c1);
System.out.println(c2);
System.out.println(c3);
}
}

案例:晚会案例
1、编写晚会程序,测试程序
2、为了程序更好维护和扩展 , 做什么与怎么做 分离
问题:在晚会类 维护所有出演演员 ---- 不想修改晚会类 ---- 工厂结构 (中介)
3、用工厂 将 做什么 与 怎么做 进行解耦合
4、将演员实例类 写入配置文件

工厂 + 反射 + 配置文件 --- 编写便于扩展、便于维护程序


今天练习总结
1、泛型:遍历类型安全 List、Set 、Map ; 自定义通过程序 数据交换位置、倒序 ; 理解 通配符和上下边界使用
2、枚举:枚举由来(员工角色案例); weekday练习 ;枚举对象和名称、下标转换
3、for/in : for/in 内存练习图解、让Class用于forin (Car 案例)、移除练习
4、静态导入、自动装箱、拆箱、可变参数 --- 简单看看即可
5、反射API 最后要求方法为主 ---- 晚会案例

*** 有追求同学:XmlPullUtils通用程序(反射+泛型) ------ 编写一个工具类 以后pull解析

猜你喜欢

转载自h496950806.iteye.com/blog/2041547