黑马程序员-枚举与反射

------- android培训java培训、期待与您交流! ----------

1,为什么要使用枚举?

       枚举就是要让某个类型的变量的取值,只能是若干个固定值中的一个,否则,编译器就会报错。枚举可以让编译器在编译的时候就可以控制源程序中填写的非法值,普通变量的方式在开发阶段无法实现这一目标。

2,用普通类实现枚举功能,定义一个Weekday的类模拟枚举的功能。

     私有构造方法

    每个元素分别用一个公有的静态成员变量表示

    可以有若干公有方法或抽象方法。例如要提供nextDay()是必须抽象的。

           每个元素分别用一个公有的静态成员变量表示可以有若干公有方法或抽象方法。采用抽象方法定义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
 }
}

   4,枚举类的基本应用

 

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()。
   

 5,构造方法的枚举

   构造方法必须定义成私有的
   如果有多个构造方法,该如何选择哪个构造方法?
   枚举元素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");
  };
 }

}


 

     6,带有抽象方法的枚举。

定义枚举TrafficLamp
实现普通的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
	   //如果很多很小的对象,他们有很多相同的东西,那就可以把它们变成一个对象,
	   //还有些不同的东西,把他变成外部的属性,作为方法的参数传入。
   }
}



 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值