黑马程序员---Java高新技术学习笔记(前篇)

------------Android培训Java培训期待与你交流------------

1.枚举

枚举是JDK1.5出现的新特性,其实就是一个特殊的类,其中也可以定义构造方法,成员变量,普通方法和抽象方法。枚举元素必须位于方法体中最开始的部分,枚举元素列表需要用分号和其他成员分隔。把枚举重的成员方法或变量等放在枚举元素的前面,编译器会编译失败。

用普通类如何实现枚举功能,定义一个Weekday的类来模拟枚举功能。首先要有私有的构造方法,每个元素分别都是一个公有的静态成员变量表示,可以有若干公有方法或抽象方法。这是带构造方法的枚举, 构造方法必须定义成私有的,枚举元素MON和MON()的效果一样,都是调用默认的构造方法。如果希望元素调用带有参数的构造函数初始化,可以在元素名后面加上参数。带方法的枚举可以通过定义枚举TrafficLamp这个例子来验证,实现普通的next方法, 实现抽象的next方法:每个元素分别是由枚举类的子类来生成的实例对象,这些子类采用类似内部类的方式进行定义。增加上表示时间的构造方法。具体实现代码如下:

本人比较懒,把两个例子写到一个里去了~~

public class EnumTest
{
	public static void main(String[] args)
	{
		/*对应WeekDay01中的注释部分
		WeekDay01 weekDay = WeekDay01.MON;
		System.out.println(weekDay.nextDay());
		*/
		
		/*
		WeekDay weekDay02 = WeekDay.FRI;
		System.out.println(weekDay02);
		System.out.println(weekDay02.name());
		System.out.println(weekDay02.ordinal());//所在位置的index
		System.out.println(weekDay02.getClass());
		System.out.println(WeekDay.valueOf("SUN").toString());
		System.out.println(WeekDay.values().length);
		*/     //
		      //
		     //
	}       // 
	   //\\//
	public enum WeekDay
	{
		//均为静态变量
		SUN,MON(1),TUE(1),WED,THU(1),FRI,SAT;//元素类表必须置于所有方法之上,
		//如果列表后边有内容最后需要加分号,如果没有可不加。
		//加括号是为了调用有参数的构造方法
		private WeekDay()//只能是私有类型的构造函数
		{
			System.out.println("first");
		}
		
		private WeekDay(int day)
		{
			System.out.println("second");
		}
	}
	
	public enum TrafficLights
	{
		//new一个子类的实例对象<RED>,并且调用父类(TrafficLights)有参数的
		//构造方法private TrafficLights(int time)<(30)>
		RED(30)
		{
			public TrafficLights nextLamp()
			{
				return GREEN;
			}
		} , 
		GREEN(45)
		{
			public TrafficLights nextLamp()
			{
				return YELLOW;
			}
		} , 
		YELLOW(5)
		{
			public TrafficLights nextLamp()
			{
				return RED;
			}
		};
		public abstract TrafficLights nextLamp(); 
		private int time;
		private TrafficLights(int time)
		{
			this.time = time;
		}
	}
	
	
}
public abstract class WeekDay01
{
	private WeekDay01(){}//私有的构造方法
	
	public final static WeekDay01 SUN = new WeekDay01()
	{	//匿名内部类
		public WeekDay01 nextDay()//覆盖父类的方法
		{
			return MON;
		}
	};
	public final static WeekDay01 MON = new WeekDay01()
	{	//匿名内部类
		public WeekDay01 nextDay()
		{
			return SUN;
		}
	};
	
	/*对应EnumTest中注释部分
	public final static WeekDay TUE = new WeekDay();
	public final static WeekDay WED = new WeekDay();
	public final static WeekDay THE = new WeekDay();
	public final static WeekDay FRI = new WeekDay();
	public final static WeekDay SAT = new WeekDay();
	
	public WeekDay nextDay()
	{
		if(this == SUN)
		{
			return MON;
		}
		else
		{
			return SUN;
		}
	}
	*/
	
	public abstract WeekDay01 nextDay();//因为是抽象方法,所以其所在的类也必需要变成是抽象的
	
	public String toString()
	{
		return this==SUN?"SUN":"MON";
	}
	
}

2.反射

反射就是把Java类中的各种成分映射成相应的java类。
例如:一个java类用一个Class类的对象来表示,一个类的组成部分:成员变量,方法,构造方法,包等信息也用一个个的Java类来表示,就像汽车是一个类,汽车中的发动机,变速箱等等也是一个个的类。表示java类的Class类显然要提供一系列的方法,来获得其中的变量,方法,构造方法,修饰符,包等
信息,这些信息就是用相应类的实例对象来表示,他们是Field,Method,Constructor,Package等。

Java类用于描述一类事物的共性,该类事物有什么属性,没有什么属性,至于这个属性的值是什么,则是由这个类的实例对象来确定的,不同的实例对象有着不同的属性值。java程序中的各个java类,它们是否属于同一类事物,是不是可以用一个类来描述这类事物呢?这个类的名字就是Class,要注意与小写class关键字的区别。

字节码:当在源程序中用到了Person这个类的时候,首先要从硬盘上把这个类的二进制代码加载到内存中,才能去创建一个个的对象,当程序中用到了多个类时候,内存中就会有多个相应的字节码,每一个字节码就是Class的实例对象总之,只要是在源程序中出现的类型,都有各自的Class实例对象。 

Person p1 = new Person();
Person p2 = new Person();

Class cls1 = Date.class(内存中)Date类字节码1;
Class cls2 = Person.calss(内存中)Person类字节码2;
 
获取字节码的方式(3种)
1.对象.getClass() 比如p1.getClass() / new Date().getClass()
2.Class.forName("java.lang.String")-->静态方法
<两种情况>1.此类的字节码已经加载到内存中了,用时可直接返回
2.此类的字节码还没有加载到内存中,便用类加载器将其加载到内存中缓存起来
 3.类名.class 比如System.class

有关以上内容的一些具体实现代码:

import java.lang.reflect.*;
//import java.lang.reflect.Field;
//import java.lang.reflect.Method;

public class ReflectTest
{
	public static void main(String[] args) throws Exception
	{
		String str1 = "abc";
		Class cls1 = str1.getClass();
		Class cls2 = String.class;
		Class cls3 = Class.forName("java.lang.String");
		System.out.println(cls1==cls2);
		System.out.println(cls2==cls3);
		System.out.println(cls1==cls3);
	    
		//System.out.println(cls1.toString());
		System.out.println(cls1.isPrimitive());//判断是否是基本类型的字节码
		System.out.println(int.class.isPrimitive());//int型为基本类型的字节码
		System.out.println(int.class==Integer.class);//int和Integer的类型不同
		System.out.println(int.class==Integer.TYPE);
		System.out.println(int[].class.isPrimitive());//数组不是原始类型
		System.out.println(int[].class.isArray());
		
		/*================================华丽的分割线====================================*/
		
		//new String(new StringBuffer("abc"));
		//新new一个对象,调用了StringBuffer构造方法
		//用反射的方式实现形同的效果
		
		Constructor constructor1 = String.class.getConstructor(StringBuffer.class);
		
		//Constructor类型的对象,此句中的StringBuffer表示选择哪种构造方法
		//constructor1此时只是一堆存在内存中没有实例化的二进制代码
		//(String类中StringBuffer构造方法的字节码),下一条语句才会进行实例化
		
		String str = (String)constructor1.newInstance(new StringBuffer("abc"));
		
		//此句中的StringBuffer表示用此构造方法是需要传一个StringBuffer进去
		
		/*
		 * 泛型的应用省去了类型转换之苦
		Constructor<String> cons = String.class.getConstructor(StringBuffer.class);
		String strr = cons.newInstance(new StringBuffer("abcd"));
		*/
		
		System.out.println("str:"+str);
		System.out.println(str.charAt(1));
		
		Object obj = constructor1.newInstance(new StringBuffer("abc"));
		System.out.println("obj:"+obj);
		System.out.println(obj.toString());
		
		/*================================华丽的分割线====================================*/
		/*//成员变量的反射
		 * Field类代表某一个类中的一个成员变量
		 * 以下代码对应ReflectPoint.java文件
		 */
		ReflectPoint pt1 = new ReflectPoint(3,5);
		Field fieldY = pt1.getClass().getField("y");//只用于可见变量
		//此时fieldY只是代表一个实例化的变量,不代表一个具体的值
		//也就是说他不是对象身上的变量,而是类上的变量,需要实例化才有值
		System.out.println(fieldY.get(pt1));
		//需要用get方法来获取指定变量的值
		Field fieldX = pt1.getClass().getDeclaredField("x");//可见变量和非可见变量均可
		fieldX.setAccessible(true);//暴力反射
		System.out.println(fieldX.get(pt1));
		
		/*================================华丽的分割线====================================*/
		//成员变量的反射
		changeStringValue(pt1);
		System.out.println("pt1:"+pt1);
		
		/*================================华丽的分割线====================================*/
		
		//成员方法的反射
		//利用反射的方法调用字符串中的字符
		Method methodCharAt = String.class.getMethod("charAt" , int.class);
		//调用Str1.charAt(1)中的字符b
		System.out.println(methodCharAt.invoke(str1 , 1));
		System.out.println(methodCharAt.invoke(null , 1));//若第一个参数为null,说明该方法为静态
		//按照JDK 1.4 version的方法调用str1中的字符c
		//Object[]{2}) object类型的数组,里面只有一个Integer对象2,数组长度为1
		System.out.println(methodCharAt.invoke(str1, new Object[]{2}));//JDK 1.4 version
		
		/*================================华丽的分割线====================================*/
		//用反射方式执行某个类中的main方法,为什么?
		
		//CodeSegment
		/*
		String startingClassName = args[0];
		Method mainMethod = Class.forName(startingClassName).getMethod("main", String[].class);
		mainMethod.invoke(null, new String[]{"111","222","333"});
		调用此段代码是会出现参数个数不对的异常,原因是new String[]{"111","222","333"}在JDK1.5+
		中为了兼容JDK1.4,JVM在接受此数组是默认为object的数组,进行拆包从而不会当成一个参数,
		此数组会被打开数组中的元素分别作为一个参数,从而有三个参数
		解决办法:
		1.再次打包
		new Object[]{new String[]{"111","222","333"}}
		即使String数组被打开,仍然是一个Object数组
		2.(Object)new String[]{"111","222","333"}
		对编译器指明此参数为一个对象,而不是数组,不需要进行拆包
		*/
		
		//main方法是静态方法,不需要传递对象
		//目标:根据用户提供的类名,去执行该类中的main方法
		TestArguments.main(new String[]{"111","222","333"});
		
		/*================================华丽的分割线====================================*/
		
	}

	private static void changeStringValue(Object obj) throws Exception
	{
		//将对象中所有的String类型的变量进行扫描
		Field[] fields = obj.getClass().getFields();
		//先得到obj所属的那份字节码,再得到所属的字段,对字段进行迭代
		for(Field field : fields)
		{
			//if(field.getType().equals(String.class))不建议使用
			//因为只需要得到object中String类型的字段,所以将字段中的所有元素的类型通过
			//field.getType()方法利用迭代方式逐次
			//和String类型的字节码进行比较,相同的保留,不同的舍去
			if(field.getType() == String.class)//字节码要用等号比较,专业
			{
				String oldValue = (String)field.get(obj);//得到字段中String类型元素
				String newValue = oldValue.replace('b', 'a');//将字符串中目标字符进行替换
				field.set(obj, newValue);//将生成的新字符串赋值给obj
			}
		}	
	}
}

class TestArguments
{
	public static void main(String[] args)
	{
		for(String arg : args)
		{
			System.out.println(arg);
		}
	}
}
------------Android培训 Java培训 期待与你交流------------







 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值