java1.5高新技术
1.1 导入包和静态导入
1.1.1 import语句可以导入一个类或某个包中的所有类。例如:import java.util.Date; 导入了Date这个类
import java.util.*; 表示导入了util这个包下所有的类
1.1.2 import static语句导入一个类中的某个静态方法或所有静态方法。
import static java.lang.Math.*; 导入Math类中的所有静态方法
1.2 可变参数
1.2.1 一个方法接收的参数个数不固定,可以采用可变参数列表。例如:System.out.println(countScore(2,3,5));
System.out.println(countScore(1,2,3,5));
1.2.2 可变参数的特点
1.2.2.1 可变参数只能出现在参数列表的最后。例如:
void method(int i,int ... y){} 可变参数在最后
void method(int ... y,int i){} 这种写法是错误的
1.2.2.2 ... 必须位于变量类型和变量名之间,不能多也不能少
1.2.2.3 调用可变参数的方法时,编译器为该可变参数隐含常见一个数组,
在方法体中以数组的形式访问可变参数。
1.3 增强for循环
1.3.1 格式:for(type 变量名 : 集合变量名){}
1.3.2 优点是简化了代码的书写,缺陷是只能用作元素的获取,不能不能操作改变其原来的集合或数组。
1.3.3 需要注意的:
迭代变量必须在()在定义,集合变量可以是数组或者实现了Iterable接口的集合类。
增强for循环结合可变参数的例子:
public class Test {
public static void main(String[] args) {
System.out.println(Sum(4,5,6));
System.out.println(Sum(4,5));
}
public static int Sum(int ... x){
int sum = 0;
for(int i :x){ // 此时x表示数组的元素。
sum += i;
}
return sum;
}
}
1.4 基本数据类型的自动拆箱与装箱
自动装箱:Integer num1 = 12;自动拆箱:System.out.println(num1 + 12);
基本数据类型的对象缓存:
Integer num1 = 12;
Integer num2 = 12; 这块相等,<=127都是真的
System.out.println(num1 == num2);
Integer num3 = 129; 这块不相等,因为是对象
Integer num4 = 129;
System.out.println(num3 == num4);
Integer num5 = Integer.valueOf(12);
Integer num6 = Integer.valueOf(12) ; 这块的道理同上
System.out.println(num5 == num6);
2. 枚举
2.1 概念:就是要让某个类型的变量的变量的取值只能为若干个固定值中的一个,否则编译就会报错。
枚举可以让编译器在编译时期就可以控制源程序中填写的非法值,普通变量的方式在开发阶段无法实现这一目标。2.2 为什么要有枚举
2.2.1 问题:要定义星期几或性别的变量,该怎么定义?假设用1-7分别表示星期一到星期日,但有人可能会写成int weekday = 0;或即使使用常量方式也无法阻止意外。
2.2.2 枚举就是要让某个类型的变量的取值只能为若干个固定值中的一个,否则,编译器就会报错。
枚举可以让编译器在编译时就可以控制源程序中填写的非法值,普通变量的方式在开发阶段无法实现这一目标。
2.3 用普通类如何实现枚举功能,定义一个Weekday的类来模拟枚举功能。
2.3.1 私有的构造方法2.3.2 每个元素分别用一个公有的静态成员变量表示
2.3.3 可以有若干公有方法或抽象方法。采用抽象方法定义nextDay就将大量的if.else语句转移成了一个个独立的类。
2.3.4 枚举是一种特殊的类,其中的每个元素都是该类的一个实例对象,
例如可以调用WeekDay.SUN.getClass().getName和WeekDay.class.getName()。
public class EnumerateTest {
public static void main(String[] args) {
EnumWeekday monday = EnumWeekday.MON;
monday.nextDay();
System.out.println(EnumWeekday.valueOf("SUN")); // 匹配字符串为SUN的对象
// System.out.println(EnumWeekday.values().length); // 对象的个数
// System.out.println(EnumWeekday.MON.getClass().getName()); //不加getName()为class cn.itcast.enumerate.EnumWeekday内部类
// System.out.println(EnumWeekday.class.getName()); // 在上面的结果少了内部类
}
}
abstract class Weekday {
// 将构造函数私有,禁止别人new对象
private Weekday() {
}
// 建立7个内部类,表示一周的七天
public static final Weekday SUN = new Weekday(){
public void nextDay() {
System.out.println("MON");
}
};
public static final Weekday MON = new Weekday(){
@Override
public void nextDay() {
System.out.println("TUE");
}
};
public static final Weekday TUE = new Weekday(){
public void nextDay() {
System.out.println("WED");
}
};
public static final Weekday WED = new Weekday(){
public void nextDay() {
System.out.println("THU");
}
};
public static final Weekday THU = new Weekday(){
public void nextDay() {
System.out.println("FRI");
}
};
public static final Weekday FRI = new Weekday(){
public void nextDay() {
System.out.println("SAT");
}
};
public static final Weekday SAT = new Weekday(){
public void nextDay() {
System.out.println("SUN");
}
};
// 为了避免多个if语句的书写,将定义匿名内部类,覆盖此方法
public abstract void nextDay();/*{
if (this == SUN)
System.out.println("MON");
if (this == MON)
System.out.println("TUE");
if (this == TUE)
System.out.println("WED");
if (this == WED)
System.out.println("THU");
if (this == THU)
System.out.println("FRI");
if (this == FRI)
System.out.println("SAT");
if (this == SAT)
System.out.println("SUN");
}*/
// 复写toString方法
public String toString() {
if (this == SUN)
return "SUN";
if (this == MON)
return "MON";
if (this == TUE)
return "TUE";
if (this == WED)
return "WED";
if (this == THU)
return "THU";
if (this == FRI)
return "FRI";
else
return "SAT";
}
}
2.4 枚举的高级应用
2.4.1 枚举就相当于一个类,其中也可以定义构造方法、成员变量、普通方法和抽象方法。2.4.2 枚举元素必须位于枚举体中的最开始部分,枚举元素列表的后要有分号与其他成员分隔。
把枚举中的成员方法或变量等放在枚举元素的前面,编译器报告错误。
2.4.3 带构造方法的枚举
2.4.3.1 构造方法必须定义成私有的
2.4.3.2 如果有多个构造方法,该如何选择哪个构造方法?
2.4.3.3 枚举元素MON和MON()的效果一样,都是调用默认的构造方法。
2.4.4 带方法的枚举
2.4.4.1 定义枚举TrafficLamp
2.4.4.2 实现普通的next方法
2.4.4.3 实现抽象的next方法:每个元素分别是由枚举类的子类来生成的实例对象,
这些子类采用类似内部类的方式进行定义。
2.4.4.4 增加上表示时间的构造方法
2.5 枚举只有一个成员时,就可以作为一种单例的实现方式。
/*
* 要求:用枚举实现一个单例设计模式。
* 步骤:
* 1,定义一个内部的枚举类
* 2,定义元素列表,其中只有一个元素。
* 3,将构造函数私有
* 4,提供对外访问静态方法
*/
public class Single4Enum {
public static void main(String[] args) {
Single1 single = Single1.getSingle1();
System.out.println(single);
}
// 定义一个内部的枚举类
public enum Single1{
// 定义元素列表,其中只有一个元素
SINGLE;
// 将构造函数私有
private Single1(){}
// 提供抽象的对外访问静态方法
public static Single1 getSingle1(){
System.out.println("获得此对象了吧!");
return SINGLE;
}
}
}
3. 反射
3.1 概念:反射就是把Java类中的各种成分映射成相应的java类。
例如,一个Java类中用一个Class类的对象来表示,一个类中的组成部分:成员变量,方法,构造方法,包等等信息也用一个个的Java类来表示,就像汽车是一个类,汽车中的发动机,变速箱等等也是一个个的类。
表示java类的Class类显然要提供一系列的方法,来获得其中的变量,方法,构造方法,修饰符,包等信息,
这些信息就是用相应类的实例对象来表示,它们是Field、Method、Contructor、Package等等。
3.2 一个类中的每个成员都可以用相应的反射API类的一个实例对象来表示,通过调用Class类的方法可以得到这些实例对象。
3.3 Class类,反射的基石
3.3.1 Class类代表Java类,它的各个实例对象又分别对应什么呢?对应各个类在内存中的字节码,例如,Person类的字节码,ArrayList类的字节码,等等。
一个类被类加载器加载到内存中,占用一片存储空间,这个空间里面的内容就是类的字节码,不同的类的字节码是不同的,
所以它们在内存中的内容是不同的,这一个个的空间可分别用一个个的对象来表示,这些对象显然具有相同的类型,
这个类型是什么呢?
3.3.2 Class类没有显示的构造函数,字节码文件就是Class类的一个对象。
3.3.3 得到各个字节码对应的实例对象(Class类型)的三种形式。
3.3.3.1 类名.class 例如: System.class
3.3.3.2 对象.getClass() 例如: new Date().getClass()
3.3.3.3 Class.forName("类的全名") 例如:Class.forName("java.util.Date")
3.3.4 Class类包含九个原始Class实例对象,可通过 Class.isprimitive()查看。
Int.Class == Integer.TYPE;
3.3.5 在源程序中出现的类型,都有各自的Class实例对象。如int[].
3.4 Constructor类:代表某个类中的构造方法。
3.4.1 得到某个类所有的构造方法:Constructor[] con = Class.forName("java.lang.String").getConstructor();
3.4.2 通过反射创建实例对象。Class.newInstance()方法。例:
String str = (String)Class.forName("java.lang.String").newInstance();
该方法内部先得到默认的构造方法,然后用该构造方法创建实例对象,方法内部的具体
代码用到了缓存机制来保存默认构造方法的实例。
/*
* 要求:练习反射中的类Constructor中的方法
* newInstance(Object... initargs)创建相应类的实例对象
* 该方法内部先得到默认的构造方法,然后用该构造方法创建实例对象,
* 方法内部的具体代码用到了缓存机制来保存默认构造方法的实例对象
*/
public class ConstructorTest {
public static void main(String[] args) throws Exception {
// 中间省去了Constructor这一过程只能通过默认的构造来创建实例对象
String s = (String)String.class.newInstance();
}
}
3.5 Field类:代表某个类中的一个成员变量。
例如: 字节码实例.getField(”成员变量名“) 字节码.getDeclareField(”成员变量名“)3.5.1 得到的是对应字节码的类的成员变量的声明,而不是具体是值。
3.5.2 可通过get(对象)来得到对象对应的共有属性的值.
3.5.3 set(对象,newValue) 可以给对象的属性赋一个新的值,包括final的值。
3.5.4 得到私有属性的值的方式:
3.5.4.1 字节码.getDeclareField(”成员变量名“) 得到属性的定义。
3.5.4.2 通过设置 setAccessible(true)使用get(对象)可获取私有变量的值。
/*
要求:将任意一个对象中所有的String类型的成员变量所对应的字符串内容中的b改成a
分析:
1,通过对象得到相应类的字节码 对象.getClass();
2,通过字节码对象获得的String类型的字段存于数组中(属性)对象 字节码对象.getFields()
3, 遍历数组,将属性对象转成对应的字段,判断出是否是String类型的 if(getType == String.class) 用==是因为就只产生了一个字节码文件
4, 获取对象的对应属性值,将所有的字符b改成a replace(b,a);
5,将对象对应的值设置成新值
*/
import java.lang.reflect.*;
class FieldTest
{
public static void main(String[] args) throws Exception{
Play p = new Play();
Change ch = new Change();
ch.b_a(p);
System.out.println(p);
}
}
class Change
{
public void b_a(Object p) throws Exception{
// 通过对象得到相应类的字节码 对象.getClass();
Class cla = p.getClass();
// 通过字节码对象获得的String类型的字段存于数组中(属性)对象
Field[] fields = cla.getDeclaredFields();
// 遍历数组将属性对象转成对应的字段,判断出是否是String类型的
for(Field field: fields){
// 判断出是否是String类型的字段
if(field.getType() == String.class){
// 设置使对象的私有变量也可以拿到
field.setAccessible(true);
// 获取相应对象的String类型属性的值
String oldValue = (String)field.get(p);
// 将获取的值做改变,存储在新的字符串对象中
String newValue = oldValue.replace('b','a');
// 设置属性的值为新的值
field.set(p,newValue);
}
}
}
}
class Play
{
public String name = "baskeball";
public String id = "abcdcba";
private String name2 = "Zhangbin";
public String toString(){
return name + " : " + id + " : " + name2;
}
}
3.6 Method类:此类代表某个类中的一个成员方法。
3.6.1 得到String类的charAt()方法的对象。getMethod()方法。例: Method charAt = String.class.getMethod("charAt",int.class);
3.6.2 通过反射调用方法。 invoke(对象,1)
例: char ch = charAt.invoke(str,1); str表示对象
3.6.3 如果传递给invoke()方法的第一个参数为null,说明该Method对象对应的是一个静态方法。
因为静态方法可以不用对象调用,可以直接类型调用,所以传递对象时可以是null。
3.6.4 jdk1.4和jdk1.5的invoke方法的区别:
Jdk1.5:public Object invoke(Object obj,Object... args)
Jdk1.4:public Object invoke(Object obj,Object[] args)
即按jdk1.4的语法,需要将一个数组作为参数传递给invoke方法时,
数组中的每个元素分别对应被调用方法中的一个参数,
所以,调用charAt方法的代码也可以用Jdk1.4改写为 charAt.invoke(“str”, new Object[]{1})形式。
/*
目标:使用反射技术,通过命令行传入参数(某个类),获得该类的构造方法和普通方法
步骤:
1、建立Constructor的数组来接收获取的对象,Method数组来获取方法的对象 。
2、调用toString方法打印这些对象
*/
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
class ClassTest1 {
public static void main(String[] args) throws Exception {
// 建立Constructor的数组来接收获取的对象
Constructor[] constructor = Class.forName(args[0]).getConstructors();
for (Constructor con : constructor) {
System.out.println(con);
}
System.out.println("----------------------");
Method[] methods = Class.forName(args[0]).getMethods();
for (Method method : methods) {
System.out.println(method);
}
}
}
3.7 数组的反射
3.7.1 具有相同维数和元素类型的数组属于同一个类型,即具有相同的Class实例对象。
3.7.2 代表数组的Class实例对象的getSuperclass()方法返回的父类为Object类对应的Class。
3.7.3 基本类型的一维数组可以被当作Object类型使用,不能当作Object[]类型使用;非基本类型的一维数组,
既可以当做Object类型使用,又可以当做Object[]类型使用。
3.7.4 Arrays.asList()方法处理int[]和String[]时的差异。
3.7.5 Array工具类用于完成对数组的反射操作。
/*
* 要求:练习数组的反射操作 Array
* 通过反射得到数组的长度
* 通过反射打印数组元素
* 通过反射得到数组的类型
*/
import java.lang.reflect.Array;
public class ArrayTest {
public static void main(String[] args) {
int[][] arr = {{1,2,3,},{4,5,6},{7,8,9}};
int length = getArrayLength(arr);
System.out.println(length); // 数组长度
System.out.println(getIndexValue(arr, 2)); // 数组指定下标的值
printArray(arr); // 打印数组中所有的值
System.out.println(getArrayType(arr));
}
// 获取数组长度
static int getArrayLength(Object obj){
// 数组也是对象,判断对象是否是数组
return obj.getClass().isArray()?Array.getLength(obj):-1;
}
// 通过反射得到数组对应下标的值
static Object getIndexValue(Object obj, int index){
if(obj.getClass().isArray()){
return Array.get(obj, index);
}else{
return obj;
}
}
// 通过反射打印数组元素
static void printArray(Object obj){
if(obj.getClass().isArray()){
for (int i = 0; i < getArrayLength(obj); i++) {
System.out.println(Array.get(obj, i));
}
}
}
// 通过反射得到数组的类型
static String getArrayType(Object obj){
if(obj.getClass().isArray()){
return obj.getClass().getName();
}else{
return obj.getClass().getName();
}
}
}
总结:
1.反射知识点的学习再一次激发了我学习的热情,因为它颠覆了我之前的认识,不了解反射之前,我只知道被final修饰的变量不可修改。现在知道那是在没有反射的情况下,这算是对我已掌握知识的一次颠覆吧。
2.当时学这些知识点的时候感觉挺轻松的,也写了例子程序,当我来写这篇博客的时候,发现有些代码写不出来了,看了以前的代码才又会议起来,这边学边忘,还是要时时回顾旧知识比较重要。