黑马程序员-java专题系列之4-高新技术

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

jdk1.5新特性
1.静态导入
在jdk1.5之后提供了静态导入功能,如果一个类中的方法全部是使用static声明的静态方法,则在导入时就可以使用 import static 的方式导入格式如下
import static 包.类.*
2.可变参数
在调用一个方法时,必须根据方法的定义传递指定的参数,在jdk1.5之后产生了一个新的概念---可变参数,即方法中可以接受的方法不是固定的而是随着需要传递的,格式如下

修饰符 返回值类型 方法名称(类型... 参数名称),


在方法中传递可变参数之后,其中的参数是以数组的形式保存下来的,值得注意的是可变参数必须在方法名称的最后一个参数位置上
3.增强的for循环
数组和集合的输出一般使用for循环进行迭代,但在jdk1.5之后为了方便数组和集合的输出,提供了一种增强的for循环语法格式如下
for(数据类型 变量名称 : 数组名或集合名) {
循环内容..
} 弊端:因为这种for循环没有下标,所以无法对数组和集合的指定下标元素进行处理
4.自动装箱与自动拆箱
将一个基本数据类型变为包装类,这就是装箱,将一个包装类变为基本数据类型,这就是拆箱
在jdk1.5之前的装箱
Integer i = new Integer(8); //将一个基本数据类型变为包装类,装箱操作
int x = i.intValue(); //将一个包装类变为基本数据类型,拆箱操作
jdk1.5之后就没有这么麻烦了
Integer i = 8; //自动装箱

int x = i; //自动拆箱

   实例1

/*
			 * 
			 *jdk1.5的新特性,静态导入和可变参数 以及增强的for循环,自动装箱拆箱
			 * 
			 */
			import static java.lang.Math.*;		//导入math类中的所有静态方法
			public class StaticimportAndVarAbleArrayParameter {

				public static void main(String[] args) {
					System.out.println(max(7,9));	//这里调用了math类中的静态方法,因为使用了静态导入,所以不需要加类名.
					int sumNum = sum(4, 4, 5, 9, 8, 90, 765);	//调用sum方法
					Integer i = sumNum;		//指定装箱
					int x = i;		//自动拆箱
					System.out.println("所求之和为:"+ x);
					Integer y = sumNum;
					System.out.println(i == y);		//判断i和y是否指向同一个对象
					Integer z = 54;
					Integer o = 54; 
					System.out.println(z == o);
				}
				
				public static int sum(int x, int ...args) {		//定义了可变参数的方法
					int sum = x;
					for(int arg: args) {		//jdk1.5之后新增加的增强版for循环
						sum += arg;		//将数组中的数据进行累加
					}
					return sum;
				}
			}
值得注意的是如果自动装箱的数据是-128-127之间,则创建了一个对象后不会创建第二个,从而减少内存的使用,这称为享元模式
享元模式(英语:Flyweight Pattern)是一种软件设计模式。它使用共享物件,用来尽可能减少内存使用量以及分享资讯给尽可能多的相似物件;它适合用于当大量物件只是重复因而
导致无法令人接受的使用大量内存。通常物件中的部分状态是可以分享。常见做法是把它们放在外部数据结构,当需要使用时再将它们传递给享元。
5枚举
在jdk1.5之前,java有两种方式定义新类型:类和接口,对于大部分面向对象的编程来说,这两种方法似乎足够了,但在一些特殊情况下,这些方法就不合适。例如想定义一个color类他只能
由REF GREEN BLUE 3种值,其他任何值都是非法的,在jdk1.5之后就可以直接使用枚举来完成格式如下
修饰符 enum 枚举类型名称 {
枚举对象1,枚举对象2,枚举对象3...枚举对象n
}
以上的Color定义出来的枚举类型,其中包括RED,GREEN,BLUE三个对象
5.Enum 
使用 enum 关键字可以定义一个枚举,实际上此关键字表示的是java.lang.Enum 类型。即用 enum 声明的枚举类型相当于定义了一个类,此类默认继承java.lang.Enum 类
下面让我们来看看枚举类的主要方法在程序中的体现
实例2

/*
 * 
 *在枚举中使用构造方法 ,并实现抽象方法
 * 
 */
public class TestEnum {

	public static void main(String[] args) {
		Color[] c = Color.values();		//将枚举内容全部变为对象数组
		for(Color color: c) {
			System.out.println(color.toString() + "--" + color.name() + "---" + color.ordinal());		//对数组中的枚举对象逐一进行输出
		}
	}
}
enum Color {
	RED("红色"){
		public String getColorName() {		//枚举对象实现抽象方法
			return "红色";
		}
	}, GREEN("绿色") {
		public String getColorName() {		//枚举对象实现抽象方法
			return "绿色";
		}
	}, BLUE("蓝色") {
		public String getColorName() {		//枚举对象实现抽象方法
			return "蓝色";
		}
	};
	private String name;			//定义name属性
	private Color(String name) {
		this.name = name;
	}
	public String toString() {		//覆写toString方法
		return this.name;
	}
	public abstract String getColorName();		//定义抽象方法
}





6.java 的反射机制
通过类可以得到他的对象,反之,利用已知的对象可以得到该类的名称以及其他的一些信息,之后把信息映射成相应的java类,这就是反射
下面让我们来看看如何利用反射来实例化对象
反射是用到框架上的,举个例子来说,如果让你写一个方法,这个方法接受一个String参数,就是一个类的类名,让你返回一个使用该类名创建出的一个对象,这时候该怎么办,写程序时,你无法知道别人调用这个方法的时候究竟会传一个什么参数,所以你程序也不能写死,你也不能这么写 return new s(s是形参变量名),这是错误的,这时候就得考虑使用反射,首先利用类名拿到字节码文件,再通过getConstructor拿到构造方法,之后通过newInstance方法得到对象,后面的javaBean还有代理模式都涉及到反射
实例3
/*
 * 通过反射来实例化对象
 * 1.拿到指定类的字节码文件
 * 		三种方式可用1.Class。forName("完整的包.类名称");		这也是最常用的方式,无需创建对象就能拿到类对应的字节码文件
 * 			    2.对象.getClass();
 * 				3.类名.class;
 * 2.拿到Constructor类的对象
 * 3.调用newInstance()方法实例化对象
 * */
import java.lang.reflect.*;
public class ReflectObject {

	public static void main(String[] args) throws Exception {
		String str = (String)getObject("java.lang.String");
		System.out.println(str);
	}
	
	public static Object getObject(String className) throws Exception {
		Class class1 = Class.forName(className);
		System.out.println(class1.getPackage().getName());		//得到该类所在的包
		System.out.println("该类是不是基础数据类型     " + class1.isPrimitive());		//判断该类是不是基本数据类型,即boolean、byte、char、short、int、long、float double 和 void才返回true
																							。 
		Constructor constructor1 = class1.getConstructor(byte[].class);		//拿到指定参数的构造方法
		return constructor1.newInstance(new byte[]{97, 98, 99});		//用该方法实例化对象
	}
}

通过类的反射我们也能拿到类中的全部属性,不管是公有的,私有的,受保护的,还是默认的,甚至是父类的,全部都能够拿到
实例4

/*
 * 
 * 从反射中拿到一个类的全部属性并重新设置属性内容
 * 
 * 
 * 
 * */

import java.lang.reflect.*;
import java.io.*;
public class ReflectField {

	public static void main(String[] args) throws Exception {
		DrawCircle dc = new DrawCircle(5, 4);
		Class class1 = Class.forName("DrawCircle");			//拿到该类的字节码文件
		Field[] fields = class1.getDeclaredFields();		//拿到声明的所有属性
		for(Field field: fields) {
			System.out.println(Modifier.toString(field.getModifiers()) + " " + field.getType().getName() +" "+field.getName());
		}
		
		fields[0].setAccessible(true);		//设置该属性可以被外部访问
		fields[0].setInt(dc, 3);			//设置该属性在对象中的值
		System.out.println(fields[0].getInt(dc));
	}

}


class DrawCircle {
	
	private int x;
	public int y;
	
	public DrawCircle(int x, int y) {
		super();
		this.x = x;
		this.y = y;
	}
	public int getX() {
		return x;
	}
	public void setX(int x) {
		this.x = x;
	}
	public int getY() {
		return y;
	}
	public void setY(int y) {
		this.y = y;
	}
	
}

能取到成员变量,同理,也能取到成员方法(包括静态方法和非静态方法)
实例5

/*
 * 
 * 对静态方法及接受数组参数的成员方法进行反射
 * 
 * */

import java.lang.reflect.*;
public class ReflectMethod {

	public static void main(String[] args) throws Exception {
		Class class1 = Class.forName("java.lang.String");
		Method m1 = class1.getMethod("wait", null);
		System.out.println(m1.getName() + "   " + m1);		//可见其连父类中的方法也能够拿到
		Method m2 = class1.getMethod("indexOf", java.lang.String.class);
		Object x = m2.invoke(new String("abcde"), new String("c"));	
		System.out.println(x);
		Method m3 = class1.getMethod("valueOf", float.class);
		float f = 3.1415f;
		System.out.println(m3.invoke(null, f));		//对于静态方法的调用,对象参数直接为null即可
		Method m4 = ReflectMainMethod.class.getMethod("main", String[].class);
		Object[] str = new Object[]{new String[]{"123", "456", "789"}};		
		m4.invoke(null, str);		//调用主函数的main方法,因为是静态方法,所以第一个参数为null
		
	}
}

class ReflectMainMethod {
	public static void main(String[] args) {
		for(String str : args) {
			System.out.println(str);
		}
		//System.out.println(args);
	}
}

在这个小程序中值得注意的是,在最后调用另一个类的main方法时,在传参数的时候为什么要把 String 数组对象包在 Object 数组中呢,这是因为jdk1.5兼容jdk1.4,所以把 String
数组传过去以后,invoke()方法会将其分解为多个参数,所以就应该将其封装在 Object 数组对象中


7.用类加载器加载资源和配置文件
实例6

/*
 * 
 * 用类加载器加载资源时,一定要注意配置文件所放的位置,一般都是放在classpath路径下
 * 
 * */
import java.lang.reflect.*;
import java.util.Properties;
import java.io.*;
public class ReflectTestClassLoader {

	public static void main(String[] args) throws Exception {
		//InputStream i = new FileInputStream("config.properties");
		Properties p = new Properties();
		//p.load(i);
		//i.close();
		//System.out.println(p.getProperty("className"));		//第一种方式,利用流的方式进行加载
		InputStream i1 = Class.forName("ReflectTestClassLoader").getClassLoader().getResourceAsStream("config.properties");
		//类加载器会去classpath路径下去找config.properties文件,所以文件必须放在classpath目录下
		p.load(i1);		//加载资源
		Class class1 = Class.forName(p.getProperty("className"));
		Constructor c = class1.getConstructor(char[].class);
		System.out.println(c.newInstance("abcde".toCharArray()));
		
	}
}

8.javaBean的内省和反射
什么是javaBean: javaBean是一种特殊结构的java类,方法的命名符合特定的规律,主要用于访问类中私有的字段,javaBean的属性是根据其方法来确定的
例如getName---属性名为name
isMax-----属性名为max
setAge----属性名为age
通过javaBean调用方法有两种方式
实例7

import java.beans.PropertyDescriptor;
import java.lang.reflect.Method;
/*
 * 对javaBean类中的方法进行反射调用
 *1.这是简单的方式
 * */

public class ReflectJavaBean {

	public static void main(String[] args) throws Exception {
		MyCircle mc = new MyCircle(7, 8);
		System.out.println(getProperty(mc, "x"));
		setProperty(mc, "x", 10);
		System.out.println(getProperty(mc, "x"));
	}
	public static Object getProperty(Object obj, String propertyName) throws Exception {		//定义拿到属性值的方法
		PropertyDescriptor pd = new PropertyDescriptor(propertyName, obj.getClass());
		Method m = pd.getReadMethod();		//拿到set方法的的对象
		Object returnValue = m.invoke(obj, null);
		return returnValue;
	}
	
	public static void setProperty(Object obj, String propertyName, int newValue) throws Exception {		//定义设置属性值得方法
		PropertyDescriptor pd = new PropertyDescriptor(propertyName, obj.getClass());
		Method m = pd.getWriteMethod();		//拿到get方法的对象
		m.invoke(obj, newValue);		//调用set方法,设置新值
	}
}

实例8

import java.beans.BeanInfo;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Method;
/**
 *第2种方式,比第一种方式复杂 
 * 
 */
public class IntrospectorTest {

	public static void main(String[] args) throws Exception {
		BeanInfo bi = Introspector.getBeanInfo(MyCircle.class);
		PropertyDescriptor[] pd = bi.getPropertyDescriptors();
		MyCircle mc = new MyCircle(3, 5);
		for(PropertyDescriptor p : pd) {
			if(p.getName().equals("x")) {
				Method writeMethod = p.getWriteMethod();
				writeMethod.invoke(mc, 7);
				Method readMethod = p.getReadMethod();
				Object info = readMethod.invoke(mc);
				System.out.println((Integer)info);
			}
		}

	}

}

/*
 * 典型javaBean的类
 * 
 * */
class MyCircle {
	private int x;
	private int y;
	public MyCircle(int x, int y) {
		super();
		this.x = x;
		this.y = y;
	}
	public int getX() {
		return x;
	}
	public void setX(int x) {
		this.x = x;
	}
	public int getY() {
		return y;
	}
	public void setY(int y) {
		this.y = y;
	}
	
}

9.注解
1.初识注解
Annotation 是jdk1.5之后新增加的新特性,注解可以用来修饰属性,方法,类,在java.lang.annotation包中有一个接口是 Annotation ,只要是 Annotation 都必须实现此接口
在java.lang包中定义了三个注解
1.@Deprecated 用 @Deprecated 注释的程序元素,不鼓励程序员使用这样的元素,通常是因为它很危险或存在更好的选择。在使用不被赞成的程序元素或在不被赞成的代码中执行
重写时,编译器会发出警告。 
2.@Override 表示一个方法声明打算重写超类中的另一个方法声明。如果方法利用此注释类型进行注解但没有重写超类方法,则编译器会生成一条错误消息
3.@SuppressWarnings 指示应该在注释元素中取消显示指定的编译器警告
测试代码
实例9

/*
 * 注解的使用
 * 
 * */
public class AnnotationTest {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		AnnotationTest.sayHello();	
	}
	@Deprecated		加入此注解表明底下的这个方法已过时,不建议程序员调用此方法
	public static void sayHello() {		//eclipse会在方法上画一条横线,代表此方法已经过时
		System.out.println("hello");	
	}
	@Override		//加入此注解表明底下的这个方法是覆写的方法,如果方法覆写错误,将会报错
	public String toString() {
		return "hello world";
	}
}

自定义注解及在反射中如何取得注解的相关信息
直接用代码来说明
实例10

/*
 * 自己定义一个注解
 * 
 * */

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME)		//定义的这个注解将保留在运行时,相对应的还能指定RetentionPolicy.SOURCE,注解保留在源程序中 ,RetentionPolicy.CLASS,保留在class中     
@Target({ ElementType.METHOD,ElementType.TYPE})		//指定该注解的对象可作用的目的范围,属性值是一个数组
public @interface MyAnnotation {
	public String name() default "MyAnnotation"	;	//该属性类型是String
	public String color() default "green";		//该属性默认值是green
	public Sex sex() default Sex.man;					//该属性是枚举类型
	public MetaAnnotation annotation() default @MetaAnnotation;		//该属性是注解类型
	public int[] value();		//value是一个特殊的属性名,在创建对象时无需再前面加”属性名=“,前提是只有该属性需要设置
}

enum Sex {		//定义性别的枚举类型
	 man, woman;
}

@interface MetaAnnotation {
	public String name() default "MetaAnnotation";
}

/*
 * 测试注解的反射
 * */
import java.lang.annotation.Annotation;
import java.util.Arrays;

@MyAnnotation(name="abc", value={1, 2, 3})     //只有value属性需要设置时,无需加value=
public class AnnotationReflect {
			
	public static void main(String[] args) throws Exception {
		if(AnnotationReflect.class.isAnnotationPresent(MyAnnotation.class)) {
			MyAnnotation ma = (MyAnnotation)AnnotationReflect.class.getAnnotation(MyAnnotation.class);		//拿到指定的注解类对象
			System.out.println(ma.name());
			System.out.println(ma.sex());
			System.out.println(ma.color());
			System.out.println(ma.annotation().name());
			
			if(ma.value().getClass().isArray()) {		//判断返回值类型是不是数组
				for(int i : ma.value()) {
					System.out.print(i + " ");
				}
			} else {
				System.out.println(ma.value());
			}
		}

	}
}

总结,注解属性返回的类型只可以是8种基础类型, String  Class  枚举  注解,以及这些类型的数组
10.类加载器及其委托机制
什么是类加载器:一个程序运行后,jvm会到系统指定的classpath目录下去找程序对应的.class 文件,找到后,将其加载进内存,负责加载工作的就是类加载器,jvm可以安装多个加载器
系统默认有三个主要类加载器,每个类负责加载特定位置上的类
1.BootStrap
2.ExClassLoader
3.AppClassLoader
类加载器也是java类,只要是java类本身就要被类加载,所以必须要有第一个类加载器不是java类,这就是 BootStrap ,看下面一小段程序代码
实例11

public class ClassLoaderTest {

	public static void main(String[] args) {
		System.out.println(ClassLoaderTest.class.getClassLoader().getClass().getName());
		//打印为sun.misc.Launcher$AppClassLoader
		System.out.println(System.class.getClassLoader());	//打印为null
		ClassLoader cl = ClassLoaderTest.class.getClassLoader();
		while(cl != null) {
			System.out.println(cl.getClass().getName());
			cl = cl.getParent();
		}
	}
}

为什么第二行打印为null,因为System.class不是由java类加载器加载的 ,而是由BootStrap加载的从严格意义上来讲他不是java的类
让我们来看看类加载器的加载范围
BootStrap 加载jre/lib/rt.jar
    |
ExtClassLoader 加载jre/lib/ext/*.jar;
    |
AppClassLoader classpath指定的所有jar和目录

类加载器的委托机制
首先由加载当前线程的类加载器去加载程序中的第一个类,但不一定回成功加载,因为首先他把加载权限交给他老爸,他老爸就交到爷爷手里,一直往下找,直到找到最终类加载器
由最终类加载器在其指定的范围内查找该类对应的.class文件,如果找到了则加载,没找到,由子类加载器去加载,没找到则继续交给子类,最终到发起任务的加载器当中,如果还
没找到,则不会交给子类加载器,就会抛出异常。


11.代理的概念与作用
什么是代理:要为已经存在的多个具有相同接口的目标类的各个方法增加一些系统功能,如异常处理,日志,计算方法的运行时间等,首先要编写
与目标类具有相同接口的代理类,代理类的每个方法调用目标类的相同方法,并在调用方法时加上别的一些功能

实例12

package com.proxy.gao;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.ArrayList;
import java.util.Collection;

public class ProxyTest {

	public static void main(String[] args) throws Exception {
		Class clazzProxy1 = Proxy.getProxyClass(Collection.class.getClassLoader(), Collection.class);
		String className = clazzProxy1.getName();
		System.out.println(className);
		Constructor[] constructors = clazzProxy1.getConstructors();
		for(Constructor constructor:constructors) {
			StringBuilder sb = new StringBuilder();
			sb.append(className);
			Class[] classType = constructor.getParameterTypes();
			sb.append('(');
			for(int i = 0; i < classType.length; i++) {
				sb.append(classType[i].getName());
				if(i != classType.length - 1) {
					sb.append(',');
				}
			}
			sb.append(')');
			System.out.println(sb);
		}
		
		System.out.print("--------------look method list\n");
		
		Method[] methods = clazzProxy1.getMethods();
		for(Method method:methods) {
			String methodName = method.getName();
			StringBuilder sb = new StringBuilder();
			sb.append(methodName);
			Class[] classType = method.getParameterTypes();
			sb.append('(');
			for(int i = 0; i < classType.length; i++) {
				sb.append(classType[i].getName());
				if(i != classType.length - 1) {
					sb.append(',');
				}
			}
			sb.append(')');
			System.out.println(sb);
		}
		
		System.out.print("--------------create instance begin\n");
		
		Constructor constructor = clazzProxy1.getConstructor(InvocationHandler.class);
		
/*		class MyInvocationHander1 implements InvocationHandler{			//第一种方式,使用内部类实现InvocationHandler接口

			public Object invoke(Object proxy, Method method, Object[] args)
					throws Throwable {
				return null;
			}
		}
		Collection proxy1 = (Collection)constructor.newInstance(new MyInvocationHander1());*/
		
		Collection proxy2 = (Collection)constructor.newInstance(new InvocationHandler() {	//第二种方式,使用匿名内部类
			public Object invoke(Object proxy, Method method, Object[] args)
					throws Throwable {
				return null;
			}
		});
		//ArrayList al = new ArrayList();
		Collection proxy3 = (Collection)getProxy(new ArrayList(), new MyAdvice());
		proxy3.add("123");
		proxy3.add("456");
		proxy3.add("789");
		proxy3.clear();
		proxy3.size();
	}

	public static Object getProxy(final Object target, final Advice advice) {
		Object proxy3 = Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), new InvocationHandler() {
			public Object invoke(Object proxy, Method method, Object[] args)
					throws Throwable {
				advice.beforeMethod(method);
				Object reval = method.invoke(target, args);
				advice.afterMethod(method);
				return reval;
			}
		});
		return proxy3;
	}
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值