1,为什么要使用枚举?
2,用普通类实现枚举功能,定义一个Weekday的类模拟枚举的功能。
每个元素分别用一个公有的静态成员变量表示可以有若干公有方法或抽象方法。采用抽象方法定义nextDay就将大量的if.else语句转移成了一个个独立的类。
3,普通类模拟枚举类的基本实现
public abstract class Weekday {
private Weekday() {
};// 构造私有函数
public final static Weekday SUN = new Weekday() {
@Override
public Weekday nextDay() {// 覆盖父类的方法
// TODO Auto-generated method stub
return MON;
}
};// 子类的对象
public final static Weekday MON = new Weekday() {
@Override
public Weekday nextDay() {
// TODO Auto-generated method stub
return SUN;
}
};
public abstract Weekday nextDay();// 采用抽象方法定义nextDay()将大量的if else语句转移成了一个独立的类
/*
* public Weekday nextDay(){//不能是静态,下面是另外一种实现模式 if(this==SUN){ return MON;
* }else{ return SUN; } }
*/
public String toString() {
return this == SUN ? "SUN" : "MON";// 可以写很长的if eles
}
}
public class EnumText {
public static void main(String[] args) {
Weekday weekday = Weekday.FRI;
System.out.println(weekday);
System.out.println(weekday.name());// 名字
System.out.println(weekday.ordinal());// 排行,从0开始
System.out.println(Weekday.valueOf("FRI").name());// 把字符串变成对象
System.out.println(Weekday.values().length);// 每一个元素都存到数组中。
}
public enum Weekday {// 定义枚举类的关键字enum
SUN, MON, TUE, WED, THI, FRI, SAT;// 枚举中的每一元素,就相当于这个类的一 个实际对象
}
枚举类的values,valueOf,name,toString,ordinal等方法。
总结:枚举是一种特殊的类,其中的每个元素都是该类的一个实例对象,例如可以调用WeekDay.SUN.getClass().getName和WeekDay.class.getName()。
构造方法必须定义成私有的
如果有多个构造方法,该如何选择哪个构造方法?
枚举元素MON和MON()的效果一样,都是调用默认的构造方法
public enum Weekday {// 定义枚举类的关键字enum
SUN(0), MON(), TUE, WED, THI, FRI, SAT;// 枚举中的每一元素,就相当于这个类的一个实际对象
// 只要用到了枚举类,静态变量都是会初始化。构造方法会执行。
private Weekday() {
// 必须放在元素列表之后,元素列表之后要有分号,必须是私有。
System.out.println("first");
}
private Weekday(int day) {
System.out.println("second");
};
}
}
实现普通的next方法
实现抽象的next方法:每个元素分别是由枚举类的子类来生成的实例对象,这些子类采用类似内部类的方式进行定义。
增加上表示时间的构造方法
public enum TrafficLamp {
// 子类的对象,每个元素都是由他的子类来写的,必须完成父类的抽象方法。
RED(30) {
@Override
public TrafficLamp nextLamp() {
// TODO Auto-generated method stub
return YELLOW;
}
},
GREEN(30) {
@Override
public TrafficLamp nextLamp() {
// TODO Auto-generated method stub
return YELLOW;
}
},
YELLOW(10) {
@Override
public TrafficLamp nextLamp() {
// TODO Auto-generated method stub
return RED;
}
};
public abstract TrafficLamp nextLamp();
private int time;// 希望每个灯都有时间
private TrafficLamp(int time) {// 通过子类来调用。
this.time = time;
}
}
// 枚举只有一个成员时,就可以作为一种单例的实现方式
}
7,反射的基石 Class类
Class 的基本分析
java程序中的各个java类属于同一类实物,描述这类的java类名就是Class
人→Person java类→Class
通过Class可以得到这个类的方方面面的信息。得到类的名字,得到自己所属的包,得到自己所有的方法列表,得到自己实现的多个接口。
Person p1=new Person(); Person p2=new Person();
Class cls1=字节码1; Class cls2=字节码2;
当我们在源程序里面用到Person这个类的时候,首先要从硬盘上,把这个类的二进制类编译成class放在硬盘上后,把这些二进制代码加载到内存里面来,然后在可以创建一个个对象,每一个字节码都是Class的实例对象
得到字节码对应的实例对象(Class类型)
p1.getClass();得到所属字节码
Class.forName("java.lang.String");指定一个类的完整名称,得到这个类的字节码,作用返回字节码,反射主要用。
Data.class;类名字节调用
九个预定义Class实例对象
void 加上八个基本数据类型
public class ReflectTest {
public static void main(String[] args) throws ClassNotFoundException {
String str1 = "abc";
Class cls1 = str1.getClass();
Class cls2 = String.class;
Class cls3 = Class.forName("java.lang.String");
System.out.println(cls1 == cls2);//true
System.out.println(cls2 == cls3);//true
System.out.println(cls1.isPrimitive());//是否是原始类型,显然false
System.out.println(int.class.isPrimitive());//显然true;
}
}
System.out.println(int.class==Integer.TYPE);//true;常量TYPE包装类型所包装的基本类型的字节码
System.ouot.println(int[].class.isArray());//truue;
总之,只要在源程序中出现的类型,都有各自的Class实例对象,int[] ,void..
8,反射的基本概念
反射就是把java类中的各种成分映射成相应的java类
例如 getMethod返回Method类型,Method是一种类型。getField返回Field类型,一个java类有很多东西,每一个东西都用对应的类来表示。学习反射,就是把java类中的每一个成分解析成相应的类。这就是Field,Method,Constructor,Package。。。得到这些对象,然后用这些对象,干一些事情。
9,Constructor类
Constructor类代表某个类的一个构造方法
得到某个类的多有构造方法
Constructor[] construcors=Class.forName("java.lang.String").getConstructors();
得到某一个构造方法
例如:Constructor constuctor=Class.forName("java.lang.String").getConstructor(StringBuffer.class);
//获得方法时要用到类型
创建实例对象
通常方式:String str=new String(new StringBuffer("abc"));
反射方式:String str=(String)constructor.newInstanct(new StringBuffer("abc"));
//调用获得的方法时要用到上面相同的实例对象
Class.newInstance()方法
该方法内部先得到默认的构造方法,然后用该调用方法创建实例对象
该方法内部的具体代码是怎么写的呢?用到了缓存机制来保存默认构造方法的实例对象。
String obj=(String)Class.forName("java.lang.String").newInstance();
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
public class ReflectTest {
public static void main(String[] args) throws ClassNotFoundException, SecurityException, NoSuchMethodException, IllegalArgumentException, InstantiationException, IllegalAccessException, InvocationTargetException {
//new String(new StringBuffer("abc"));利用反射来实现同样的效果
Constructor constru1= String.class.getConstructor(StringBuffer.class);//jdk可变参数,若干个Class,表示选择那个构造方法,
String str2=(String)constru1.newInstance(new StringBuffer("abc"));//编写源程序的时候,只知道是constructor,而不知道是哪个构造方法。
}
}
10,Field类
FIEL类代表某个类中的一个成员变量
看代码实现
首先定义一个ReflectPoint类
public class ReflectPoint {
private int x;
public int y;
public ReflectPoint(int x, int y) {
super();
this.x = x;
this.y = y;
}
}
主函数
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
public class ReflectTest {
public static void main(String[] args) throws Exception {
ReflectPoint pt1 = new ReflectPoint(3, 5);
Field fieldy = pt1.getClass().getField("y");
// fieldy不是对象身上的变量,而是类上要用它去取某个对象对应的值。
System.out.println(fieldy.get(pt1));
// Field
// fieldx=pt1.getClass().getField("x");//x私有字段不能这样使用getFiled("x");,这个只能得到可见的。
// System.out.println(fieldx.get(pt1));
Field fieldx = pt1.getClass().getDeclaredField("x");
fieldx.setAccessible(true);// 暴力反射。不管同意不同意,就要去拿。
System.out.println(fieldx.get(pt1));
}
}
11,将任意一个对象中的所有String类型的成员变量所对应的字符串内容中的“b”变成“a”;
代码如下:
public class ReflectPoint {
private int x;
public int y;
public String str1 = "ball";
public String str2 = "baseketball";
public String str3 = "itcast";
public ReflectPoint(int x, int y) {
super();
this.x = x;
this.y = y;
}
@Override
public String toString(){
return str1+":"+str2+":"+str3;
}
}
主函数代码如下:
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
public class ReflectTest {
public static void main(String[] args) throws Exception {
ReflectPoint pt1 = new ReflectPoint(3, 5);
changeStringValue(pt1);
System.out.println(pt1);
}
private static void changeStringValue(Object obj) throws IllegalArgumentException, IllegalAccessException {
// TODO Auto-generated method stub
//扫描这个类中的所有String类型的变量
Field[] fields=obj.getClass().getFields();//得到字节码,所有可见字段
for(Field field:fields){
//field.getType().equals(String.class),可以用到等号
if(field.getType()==String.class){//对字节码的比较用等号比较较好,用equals也可,语义不准确
String oldValue=(String)field.get(obj);
String newValue=oldValue.replace('b', 'a');
field.set(obj, newValue);//把新值给对象
}
}
}
}
12,Method类
Method类代表某个类中的一个成员方法
得到类中的某一个方法
例如:Method charAt=Class.forName("java.lang.String").getMethod("charAt",int.class);
调用方法
通常:System.out.println(str.charAt(1));
反射:System.out.println(charAt.invoke(str,1));
如传递给Method对象的invoke()方法的第一个参数为null,这有着什么样的意义呢?
说明该Method对象对应的是一个静态方法。
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class ReflectTest {
public static void main(String[] args) throws Exception {
String str1="abc";
//str1.charAt(1);
Method methodCharAt=String.class.getMethod("charAt", int.class);
System.out.println(methodCharAt.invoke(str1, 1));//,Method的方法,调用,方法对象的方法
System.out.println(methodCharAt.invoke(null, 1));//如果第一个参数是null,这个方法是静态的,静态方法调用不需要对象。
}
private static void changeStringValue(Object obj) throws IllegalArgumentException, IllegalAccessException {
// TODO Auto-generated method stub
//扫描这个类中的所有String类型的变量
Field[] fields=obj.getClass().getFields();//得到字节码,所有可见字段
for(Field field:fields){
//field.getType().equals(String.class),可以用到等号
if(field.getType()==String.class){//对字节码的比较用等号比较较好,用equals也可,语义不准确
String oldValue=(String)field.get(obj);
String newValue=oldValue.replace('b', 'a');
field.set(obj, newValue);//把新值给对象
}
}
}
}
写一个程序根据用户提供类的类名,去执行该类中的main方法
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
//写一个程序这个程序根据用户提供的类名,去执行该类中的main方法。
public class ReflectText2 {
public static void main(String[] args) throws SecurityException,
NoSuchMethodException, ClassNotFoundException,
IllegalArgumentException, IllegalAccessException,
InvocationTargetException {
// 普通 方式 TestArguments.mian(new String[]{"aa","bb"});
// 反射凡是
String startingClassName = args[0];
Method mainmethod = Class.forName(startingClassName).getMethod("main",
String[].class);
// 编译器会做特殊处理,编译时不把参数当做数组看待,也就不会数组打散成若干个元素了mianmethod.invoke(null,new
// String[]{"aa","bb"});
// mainmethod.invoke(null, new Object[]{new String[]{"aa","bb"}});
mainmethod.invoke(null, (Object) new String[] { "aa", "bb" });
}
}
class TestArguments {
public static void mian(String[] args1) {
// TODO Auto-generated method stub
for (String arg : args1) {
System.out.println(arg);
}
}
}
13,数组与Object的关系及其反射类型
import java.util.Arrays;
public class ReflectTest3 {
public static void main(String[] args) {
int[] a1 = new int[] { 1, 2, 3 };// 维数相同,类型相同
int[] a2 = new int[4];
int[][] a3 = new int[2][3];
String[] a4 = new String[] { "aa", "bb", "cc" };
System.out.println(a1.getClass() == a2.getClass());
// System.out.println(a1.getClass()==a4.getClass());
// System.out.println(a1.getClass()==a3.getClass());
System.out.println(a1.getClass().getName());
System.out.println(a1.getClass().getSuperclass().getName());
System.out.println(a4.getClass().getSuperclass().getName());
Object obj1 = a1;
Object obj2 = a4;
// Object[] obj3=a1;//数组里面是int此处错误,
Object obj4 = a3;// 数组里面装的是一维数组,jdk1.4
Object[] obj5 = a4;
System.out.println(a1);
System.out.println(a4);
System.out.println(Arrays.asList(a1));// 整数不转打印结果[[I@1e5e2c3] jdk1.5一个参数
System.out.println(Arrays.asList(a4));// jdk 1.4一个数组
}
}
数组的反射
具有相同维数和元素类型的数组属于同一个类型,既具有相同的Class实例对象。
代表数组的Class实例对象的getSuperClass()方法返回父类为Object类对应的Class
基本类型的一维数组可以被当做Object类型使用,不能当做Object[]类型使用:非基本类型的一维数组,既可以当做Object类型使用,又可以当做Object[]类型使用。
Arrays.asList()方法处理int[]和String[]时的差异。(如上面的代码)
Array工具类用于完成对数组的反射操作。
14, HashSet,Hashcode分析
public class ReflectPoint {
private int x;
public int y;
public String str1 = "ball";
public String str2 = "baseketball";
public String str3 = "itcast";
public ReflectPoint(int x, int y) {
super();
this.x = x;
this.y = y;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + x;
result = prime * result + y;
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
ReflectPoint other = (ReflectPoint) obj;
if (x != other.x)
return false;
if (y != other.y)
return false;
return true;
}
@Override
public String toString(){
return str1+":"+str2+":"+str3;
}
}
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
public class ReflectTest5 {
public static void main(String[] args) {
Collection collections=new HashSet();//先判断有没有,如果有,不加。
//Collection collections=new ArrayList();//有序
ReflectPoint pt1=new ReflectPoint(3,3);
ReflectPoint pt2=new ReflectPoint(5,5);
ReflectPoint pt3=new ReflectPoint(3,3);//这里pt1和pt3相同
collections.add(pt1);
collections.add(pt2);
collections.add(pt3);
collections.add(pt1);
System.out.println(collections.size());
}
}
打印 结果为2
如果去掉hashcode(),打印结果有可能是2,有可能是3;
hashCode()的作用
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
public class ReflectTest5 {
public static void main(String[] args) {
Collection collections = new HashSet();// 先判断有没有,如果有,不加。
// Collection collections=new ArrayList();//有序
ReflectPoint pt1 = new ReflectPoint(3, 3);
ReflectPoint pt2 = new ReflectPoint(5, 5);
ReflectPoint pt3 = new ReflectPoint(3, 3);// 这里pt1和pt3相同
// 如果两个对象equals相等的话,让他们的hashCode也相等。
// 经验:当一个对象被存储在HashSet集合中以后,以后就不要再修改这个对象中的某些字段了。
collections.add(pt1);
collections.add(pt2);
collections.add(pt3);
collections.add(pt1);
pt1.y = 7;
collections.remove(pt1);
System.out.println(collections.size());
}
}
打印 2
改y的值,hashCode值也变了,再删除的时候,在原来区域找pt1,pt1改变了,所以找不到,内存泄露
15, java框架的概念及反射技术开发框架的原理
使用别人写的类,有两种使用方式一种是,你去调用别人的类,二是别人调用你的类 ,
一种叫做工具 一种叫框架 * 区别:工具类被用户的类调用,而框架则是调用用户提供的类。
解决的核心问题: 因为在写程序时无法知道要被调用的类名。
所以,在程序中无法直接new * 某个类的实例对象了,而要用反射方式来做。
以上是配置文件信息。具体代码如下:
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.util.Collection;
import java.util.Properties;
/**
* 使用别人写的类,有两种使用方式一种是,你去调用别人的类,二是别人调用你的类 ,一种叫做工具 一种叫框架
* 区别:工具类被用户的类调用,而框架则是调用用户提供的类。 解决的核心问题: 因为在写程序时无法知道要被调用的类名。所以,在程序中无法直接new
* 某个类的实例对象了,而要用反射方式来做。
*
* @author Administrator
*
*/
public class TrafficLamp {
public static void main(String[] args) throws IOException,
InstantiationException, IllegalAccessException,
ClassNotFoundException {
// 得到输入流
InputStream input = new FileInputStream("config.properties");
// Properties 该类主要用于读取以项目的配置文件以.properties结尾的文件或者xml文件
Properties prop = new Properties();
prop.load(input);// Properties对象已经生成,包括文件中的数据
input.close();
String className = prop.getProperty(className);// 得到配置文件中className对应的值。
Collection collections = (Collection) Class.forName(className)// 通过得到实力对象
.newInstance();
ReflectPoint pt1 = new ReflectPoint(3, 4);
ReflectPoint pt2 = new ReflectPoint(3, 4);
System.out.println(collections.size());
}
}
类加载器的方法得到流对象,一定要记住用完整的路径,
但完整的路径不是硬编码,而是运算出来的,上面的实例可以见下面的优化。
package com.itcast.zhang;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.util.Collection;
import java.util.Properties;
public class ReflectTest6 {
public static void main(String[] args) throws Exception {
// 一定要记住用完整的路径,但完整的路径不是硬编码,而是运算出来的
// 类加载器 加载配置文件
// InputStream input = new FileInputStream("config.properties");
// 在classPath指定的目录下,逐一的去查找你要加载的文件。
InputStream input = ReflectTest6.class.getClassLoader()
.getResourceAsStream("com/itcast/zhang/config.properties");
Properties prop = new Properties();
prop.load(input);
input.close();
String className = prop.getProperty("className");
Collection collections = (Collection) Class.forName(className)
.newInstance();
ReflectPoint pt1 = new ReflectPoint(3, 4);
ReflectPoint pt2 = new ReflectPoint(3, 4);
System.out.println(collections.size());
}
}
16, java装箱,拆箱
public class Test1 {
public static void main(String[] args) {
/* Integer i=13;//装箱
System.out.println(i+12);//拆箱
Integer i1=13;//装成Integer对象
Integer i2=13;//装成Integer对象
System.out.println(i1==i2);//打印true;
*/
String s1=new String("abc");
String s2=new String("abc");
Integer i1=137;//装成Integer对象
Integer i2=137;//装成Integer对象
//-128- 127 会缓存起来。比较小的使用频率很高。
System.out.println(i1==i2);//打印false;
//享元设计模式flywight
//如果很多很小的对象,他们有很多相同的东西,那就可以把它们变成一个对象,
//还有些不同的东西,把他变成外部的属性,作为方法的参数传入。
}
}