17 Java基础加强(上)

----------- android培训java培训、java学习型技术博客、期待与您交流! ------------


1、静态导入
    import语句可以导入一个类或一个包中所有的类,并不占用资源,只是在编写程序时少写一些前缀(普通导入)。
    import语句导入一个类中的某个静态方法或所有静态方法。(静态导入,jdk1.5新特性)
	例如:import static java.lang.Math.max;	导入一个静态方法
	      import static java.lang.Math.*;	导入一个类中所有的静态方法


2、可变参数(varible parameter)
    可变参数的特点:
	只能出现在参数列表的最后,参数个数可以改变,参数类型必须一致
	...位于变量类型和变量名之间,前后有无空格都可以
	调用可变参数的方法时,编译器,为该可变参数隐含创建一个数组,在方法中以数组的形式访问参数
	public static void mian(String[] args) {
		add(2,3,5);
	}
	public static int add(int x,int... args) {	//求和
		int sum = x;				//定义变量sum,初始化值为x
		for (int i = 0;i < args.length;i++) {	//可变参数args为一个数组,遍历数组,获取元素args[i]
			sum += args[i];			//获取到一个元素就与原来的和相加,sum记录变化后结果
		}
		return sum;				//返回最后参数的和
	}

3、增强for循环(foreach)
    语法:for(type 变量名:容器名) {...}
    注意:迭代变量必须在()中定义,容器可以是数组或实现Iterable接口的集合类
	public static int add(int x,int... args) {
		int sum = x;
		for(int arg:args) {
			sum += arg;
		}
		return sum;
	}

4、基本数据类型的自动装箱与拆箱
	Integer num1 = 3;		//自动装箱,把数字3装成Integer对象,以前是Integer(int value) 
	System.out.println(num1 + 12);	//自动拆箱,把对象转成int数,参与运算,输出结果15
	Integer num2 = 3
	System.out.println(num1 == num2)//true:num1和num2只有在-128~127范围内才相等,这个范围之间
					的数字都被封装成了固定的对象,dispaly(int x,int y)区分不同。

	享元模式(Flyweight Patthern):很多对象有很多相同的属性,把这些对象封装成一个对象,把不同
					的属性作为该对象方法的参数,称之为外部状态。相同属性称之
					为对象的内部状态。(例如Integer -128~127之间的数字)
				      
	String s1 = new String("abc");
	String s2 = new String("abc");
	System.out.println(s1.equals(s2));//false:String类一旦创建就不会改变,这里是两个对象比较

	享元设计模式:基本数据数据类型都有一个取值范围,在自动装箱时,把这个范围的数字都封装成一个对象。
		      这样这些数字就共有一个对象,而不是创建新的对象。这样节省了内存空间。
		      如果很多对象有很多相同的地方,我们可以把它们变成一个对象,那些不同的东西变成外部的属性
		      作为方法的参数传入。(Flywait Pattern)
		 

5、枚举(enum):
    枚举相当于一个类,其中也可以定义构造方法、成员变量、普通方法和抽象方法。
    枚举元素必须位于枚举体中的最开始部分,枚举元素列表后面要有分号与其它成员分离。把枚举中的成员方法
	和变量等放在枚举列表前面,编译器会报告错误。
    
    为什么要有枚举?
	枚举是为了让某个类型变量的取值,只能是该类型若干个固定值中的一个,否则编译器就报错。枚举可以让编译器
	在编译时,就可以控制源程序中的非法值,普通变量的方式在开发阶段无法实现这一目标。
	普通类模拟枚举(WeekDay):
	    * 私有的构造方法
	    * 每个元素分别用一个公有的静态成员变量表示
	    * 可以有若干个公有方法或抽象方法,例如要提供nextDay()必须是抽象的,采用抽象方法定义nextDay()
		将大量的if else语句转移成了一个个独立的类。
	    public /*abstract*/ class WeekDay {
		private WeekDay() {};					//构造方法私有,
		/*
		public final static WeekDay SUN = new WeekDay() {	//常量是该抽象类匿名子类的实例对象
		    public WeekDay nextDay() { return MON;}		//子类实现父类的抽象方法,简化if else
		};
		public final static WeekDay MON = new WeekDay() {
		    public WeekDay nextDay() { return SUN;}
		};
		public abstract WeekDay nextDay() {};			//父类的方法抽象
		
		*/

		public final static WeekDay SUN = new WeekDay();//定义全局变量,是一个对象 
		public final static WeekDay MON = new WeekDay();
		public WeekDay nextDay() {
		    if(this == SUN)
			retunr MON;
		    return SUN;
		}
		public String toString() {
		    return this == SUN ? "SUN" : MON;
		}
	    }

	    public class EnumDemo {
		public static void mian(String[] args) {
		    /*WeekDay weekday = WeekDay.SUN;		//只能方法问WeekDay类型的全局常量
		    WeekDay weekday = WeekDay.MON;
		    System.out.println(weekday.nextDay());*/

		    WeekDay weekday2 = WeekDay.FRI;
		    System.out.println(weekday2);		//打印变量,自动调用toSting()
		    System.out.println(weekday2.name());	//输出变量的名称,name()
		    System.out.println(weekday2.ordinal());	//输出常量在第几个位置,ordinal()
		    System.out.println(WeekDay2.valueOf(“SUN”));//把字符串变成对应的枚举元素,static valueOf()
		    System.out.println(WeekDay2.values());	//得到一个所有枚举元素组成的数组
		}
		public enum WeekDay {		//定义枚举类	
		    SUN,MON,TUE,WED,THU,FRI,SAT;//元素列表都是全局常量,也是对象,
		    带构造方法的枚举:
			构造方法必须是私有的(默认)
			如果有多个构造方法,在枚举元素后面添加括号(parameter),指定构造方法
			枚举类一创建对象,静态变量(枚举元素)就初始化,MON和MON()都是调用默认无参构造方法。
		    private WeekDay() {System.out.println("无参构造器");} //SUN、SUN()都可以调用默认无参构造器
		    private WeekDay(int day) {System.out.println("有参构造器");}
									  //SUN(1),指定参数列表,调用有参构造器
		    带抽象方法的枚举:
			定义枚举TrafficLamp
			实现普通的next()方法
			实现抽象的next()方法,每个元素分别是由枚举类的子来生成的实力对象,这些类采用类似内部类的方式定义。
			增加表示时间的构造方法。
		public enum TrafficLamp {
		    RED(30) {	//RED{}是该类的匿名子类实例对象,子类是一个匿名内部类,重写父类中的抽象方法
				//RED(30)是调用父类有参构造器
			public TrafficLamp nextLamp() {return GREEN;}
		    },		//常量在class文件中显示为EnumDemo$TrafficLamp$1、$2、$3
		    GREEN(45) {
			public TrafficLamp nextLamp() {return YELLOW;}
		    },
		    YELLOW(5) {
			public  TrafficLamp nextLamp() {return RED;}
		    };
		    public abstract TrafficLamp nextLamp();
		    private int time;	//每一个灯都有时间time,	
		    private TrafficLamp(time) {this.time = time;}//通过该类构造函数对time进行初始化
		}
	    }
	枚举:
	    内部类提高了编程的思想
	    类的方法可以返回本类类型
	    类中可以定义静态常量,常量指向是本类子类的实例对象

    枚举只有一个成员时,可以作为一种单例的实现方式。
	public enum SingletonPattern {
	    private SingletonPattern() {};
	    private final static SingletonPattern sp = new SingletonPattern();
	    public static SingletonPattern getInstance() {return sp;};
	    /*
	    private SingletonPattern() {};//单例用枚举来实现,无参构造器默认私有
	    public fianl static SingletonPattern sp = new SingletonPattern() {
	        public SingltonPattern getInstance() {return sp;}
	    };
	    public abstract SingletonPattern getInstance();
	    */
	}


6、反射
    反射的基石-->Class类:
	java程序中的各个java类都属于同一类事物,描述这类事物的java类名就是Class。Class类描述了类的名字,
	类的访问属性,类所属的包名,字段名称的列表,方法名称的列表等等。
    Class类代表java类,它的实例对象分别对应各个类在内存中的字节码。
    一个类被类加载器加载到内存中,占用一片存储空间,这个空间里面的内容就是字节码。不同类的字节码是不同的,
    所以它们在内存中的内容也是不同的。这一个个的内存空间分别用一个个的对象来表示,这些对象显然具有相同的
    类型,这个类型就是Class类型。通过反射就可以操作Class类。
    反射就是把java类中的各种成分映射成相应的java类,把内存中加载的“.class文件”封装成对象(Class),从而
    使用其中的构造函数(Constructor)、字段(Field,成员变量)以及方法(Method)。在程序的运行过程中可以
    通过一个名字找到指定的类,创建该类对象,反射jdk1.2就出现了,但会导致程序性能严重下降。
	
    Class:获取Class对象:代表内存中的字节码文件(.class文件,也就是类)
	类名.class:直接获取指定类的Class对象,内存中不存在则从硬盘加载。如:System.class()
	对象.getClass():获取指定对象的所属的Class对象(字节码文件)如:new Date().getClass()
	Class.forName(String classname):根据类名获取Class对象,jvm中没有用类加载器从硬盘加载缓存在jvm中	 
						如:Class.forName("java.lang.util")
	    String str = "abc";
	    Class c1 = String.class;
	    Class c2 = str.getClass();
	    Class c3 = Class.forName("java.lang.String");
	    System.out.println(c1 == c2);// true
	    System.out.println(c1 == c3);// true
	    System.out.println(c1.isPrimitive());	   // false:该方法判断字节码是不是原始类型
	    System.out.println(int.calss.isPrimitive());   // true
	    System.out.println(int.class == Integer.class);// fals
	    System.out.println(int.class == Integer.TYPE); // true
	    System.out.println(int[].class.isPrimitive()); // false
	    System.out.println(int[].class.isArray());	   // true 数组类型的字节码文件
	    只要是在源程序中出现的类型都有各自的Class实例对象,例如:int[],void,Integer
	    包装类内部有常量TYPE来表示基本数据类型的字节码
	    通过Class创建对象:Class.newInstance(),内部通过无参构造函数创建,没有无参构造函数则无法使用

    Constructor:构造方法的反射
        Class.getConstructors:获取某个类所有的构造方法字节码文件,返回Constructor类型的数组
	Class.getConstructor(Class...) :获取指定的构造函数
	String.class.Constructor(Class[] parameterTypes);(jdk5.0之前用数组来表示参数可变)
	Constructor.newIntance(Object...) :调用构造函数的方法传入实参建立对象

    Field(字段):成员变量的反射
   	Class.getField(String):根据字段名获取对象指定的字段(Field)(包括继承, 但不包括私有)
	Class.getDeclaredField(String):获取所有在当前类中声明的属性(不包括继承, 但包括私有)
	Filed.setAccessible(boolean) 设置该属性是否可以访问
	Field.set(Object, Object) 设置指定对象的属性为指定的值
	Filed.get(Object) 获取指定对象的属性值

    Method:成员方法的反射
        Class.getMethod(String, Class...) 根据名字和参数类型获取方法(可见的)
	Class.getDeclaredMethod(String, Class...) 根据名字和参数类型获取方法(该类中声明的)
	Method.setAccessible(boolean) 设置方法是否可以访问
	Method.invoke(Object, Object...) 在指定对象上执行方法, 传入若干参数

    参数为数组的成员方法的反射(以main函数为例):
        String startClassName = args[0];
	Method mainMethod = Class.forName(startClassName).getMethod("main",String[].class)
	mainMethod.invoke(null,new Object[]{new String[]{"11","22","33"}});
	main方法的参数是一个字符串数组,jdk1.5的语法是把整个数组看成一个参数,但是1.4的语法是是把
	数组中的每一个元素看成一个参数。为了兼容性考虑,按照1.4的语法把数组打散成若干个独立的参数。
	所以在使用invoke()方法时,new String[]{"11","22","3"}作为参数传递就会出现类型不对的问题。
	这时可以将它当成数组中的一个元素看待,即new Object[]{new String[]{"11","22","33"}}
	另外一种解决方式就是把编译时不要把它当成数组看待,直接在前面加(Object)进行强转

    数组的反射:
	具有相同纬度和元素类型的数组属于同一个类型,即具有相同的Class对象
	代表数组Class实例对象的getSuperclass()方法返回的父类为Object类对应的Class
	例如:	int[] a1 = new int[3];	
		int[] a2 = new int[2];	
		int[][] a3 = new int[2][3];
		String[] str = new String[3]
		System.out.println(a1.getClass() == a2.getClass());//true
		System.out.println(a1.getClass() == a3.getClass());//false
		System.out.println(a1.getClass() == a4.getClass());//false
		System.out.println(a1.getClass().getName());	   // [I:维度和数据类型
		System.out.println(a1.getClass().getSuperclass().getName()); //java.lang.Object
		System.out.println(a4.getClass().getSuperclass().getName()); //java.lang.Object
	
	数组与Object的关系及其反射类型:
	    基本类型的一维数组尅当作Object类型使用,不能当作Object[]类型使用
	    非基本类型的一维数组,既可以当作Object类型使用,又可以当作Object[]使用
	    Object obj1 = a1;	//一维数组是Object类型的
	    Object[] obj2 = a1;	//基本数据类型不能转换为Object数组
	    Object[] obj3 = a3;	//a3是包含一个int类型一维数组的数组,这个一维数组是Object类型
	    Object[] obj4 = ar;	//String类型是Object类型
	    查看数组中的内容:
		int[] a1 = new int[]{1,2,3};	
		String[] str = new String[]{"a","b","c"};
		System.out.println(Arrays.asList(a1));// [[I@1cfb549] 转换成集合后,只有一个元素,是原数据对象
		System.out.println(Arrays.asList(str));	//[a,b,c],把数组转成集合后才能打印数组中的内容
	
	数组的反射应用
	    Arrays工具类用于完成对数组的反射操作
		Object obj = a1;
		Class clazz = obj.getClass();			//获取obj的class文件
		if(clazz.isArray) {				//如果字节码是一个数组,表示它也是一个数组
		    int len = Array.getLength(obj);		//Array.getLength(obj)获取obj的长度
	            for(int i = 0;i < len;i++) {		//遍历数组
		        System.out.println(Array.get(obj,i));	//Array.get(obj,i)获取obj对象i角标上的元素
		    }
		} else
		    System.out.println(obj);			//如果不是数组,直接输出obj对象
	    数组中的元素类型:
		不能得到整个数组的类型,但可以得到数组中元素的类型
		取出每个元素对象,对各个对象进行判断,因为每个元素的类型都可以不同。例如:
		    Object[] obj = new Object[]{"a",1};
		    //没有办法得到整个数组的类型,到底是String还是int分不清楚
		    //得到具体元素的类型:obj[0].getClass().getName()

	hashCode()分析:
	     //Collection collections = new ArrayList();集合长度为4
	    Collection collectons = new hashSet();
	    ReflectPoint pt1 = new ReflectPonint(3,3);
	    ReflectPoint pt2 = new ReflectPonint(5,5);
	    ReflectPoint pt3 = new ReflectPonint(3,3);
	    collections.add(pt1);
	    collections.add(pt2);
	    collections.add(pt3);
	    collections.add(pt1);			
	    System.out.println(collections.size());// 集合长度为3
	    //如果重写hashCode()和equals()方法,集合的长度就变为2
	    //equals()方法比较的是内容,pt1和pt2相同,只存一个
	    //如果没有重写hashCode()方法,集合长度又为3。因为pt1和pt3通过Object的hashCode()有两个不同的哈希值
	    //hashCode()的作用:
		该方法把集合分成若干个存储区域,每个对象通过哈希算法算出一个哈希值,根据算出的值把对象放入对应
		的区域,这样查找的性能就提高了。只有底层是哈希结构的集合hashCode()才有价值。
		当一个对象被存储进hashSet集合中后,不能修改对象中那些参与计算哈希值的字段(属性)。
		    例如:pt1.y = 7;	
			  collectons.remove(pt1);
			  System.out.println();	// 集合的长度仍然是重写两个方法后的长度
			  //因为属性参与了哈希值的计算,属性改变后哈希值也改变,再删除pt1就找不到对应的哈希值
			  //这时删除就不成功,会造成内存泄漏,如果反复进行这种操作,积累起来就会内存溢出。
			  //内存泄漏就是对象没有使用,占用内存空间,没有被释放。


7、框架
    反射实现框架功能:
	框架与工具类:
	    框架与工具类有区别,工具类被用户的类调用,而框架则是调用用户提供的类。
	框架要解决的核心问题:
	    简单来讲,框架程序就是要调用以后的类。因为在写程序时,无法知道要被调用的类名。所以在程序中无法直接
	    new某个类的实例对象,这时通过反射的方式实现。
	综合案例:


8、内省(IntroSpector)	
    用于对JavaBean操作。JavaBean是一个特殊的类。主要用于传递数据信息。这种Java类中的方法主要用于访问私有的字段
    且方法名符合某种命名规则。

    如果要在两个模块之间传递多个信息,可以将这些信息封装到一个JavaBean中,这种JavaBean的实例对象通常称之为
    值对象(ValueObject 简称VO)。这些信息在类中用用私有字段来存储,如果读取或设置这些字段的值,则需要通过一些
    相应的方法来访问。
    
    JavaBean的属性根据对外访问属性的方法得来,而不是根据成员变量得来。即去掉set/get后,剩下来的名称就是
    JavaBean的属性,把剩余部分的首字母改成小的,但是有条件:
	如果第二个字母是小写,首字母改成小写。	    getId-->id	
	如果第二个字母是大写,首字母保持不变,好看。getCPU->CPU

    总之,一个类被当作JavaBean使用时,JavaBean的属性是根据方法名推断出来的,它根本看不到java类的成员变量。

    一个符合JavaBean特点的类可以当作普通类使用,但把它当作JavaBean使用有以下好处:
	在JavaEE开发中,经常要使用到JavaBean。很多环境就要求按JavaBean方式进行操作。
	JDK中提供了对JavaBean进行操作的一些API,这套API就成为内省。如果我们自己通过getX方法来访问私有的成员变量
	x,难度非常大。用内省这套API操作JavaBean比用普通类的方式更方便。

    内省综合案例:
	对某个对象的属性进行获取-->用JavaBean的方式:
	    ①建立一个带有set/get方法的类实例对象obj
	    Class clazz = Object.class;
	    Object obj = clazz.newInstance();
	    ②String propertyName = "x";//假设属性名字,其实是看不到的
	    //"x"-->"X"-->"getX"-->MethodGetX-->...用反射反射的方式太麻烦
	    public static Object getProperty(Object obj,String propertyName) {
		//通过这个类的构造函数从obj对象上获取到属性propertyName
		③PropertyDescriptor pd = new PropertyDescriptor(propertyName,obj.getClass())
		④Method methodGetX = pd.getReadMethod();	//获取对象的getX()方法
		⑤Object returnValue = methodGetX.invoke(obj);	//调用获取到的方法,就获obj对象的属性值。
	    }
	    ⑥System.out.println(returnValue);
	    public static void setProperty(Object obj,String propertyName,Object value) {
		⑦Method methodSetX = pd.getWriterMethod();	//获取对象的setX()方法,这时要处理异常
		⑧methodSetX.invoke(obj,7);			//调用方法,将obj对象的属性值设为7
	    }
	对某个对象的属性复杂获取-->IntroSpector类:
	    在程序中把一个类当作JavaBean来看,调用IntroSpector.getBeanInfo()方法,得到的BeanInfo类型对象
		封装了把这个类当作JavaBean看的结果的信息。
	    采用遍历BeanInfo的所有属性来查找和设置某个对象的属性
	    public static Object getProperty(Object obj,String propertyName) {
		BeanInfo beanInfo = IntroSpector.getBeanInfo(obj.getClass());//返回obj当作JavaBean看的结果
		PropertyDescriptor[] dps = beanInfo.getPropertyDescriptors();//获取属性方法返回一个属性数组
		Object returnValue = null;					     //
		for(PropertyDescriptor pd : dps) {			     //增强for循环遍历属性数组
		    if(pd.getName().equals(propertyName)		     //获取到的属性名字和传入的相等
		    Method methodGetX = pd.getReadMethod();		     //获取对象的getX()方法
		    retVal = methodGetX.invoke(obj);	//调用获取到的方法,获取obj对象的属性值。
		}
		return returnValue;
	    }


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值