从天津转站北京,爬到中关村到直立行走
Java
复习八
Java基础加强的知识是我遇到最难的一部分,暂时无法消化得了,先过一遍,然后回过头来再看一遍……
针对下载
ppt来学习
概念:
IDE:
1.
泛型
import java.util.*;
/**
* 泛型
* @author xinglefly
* @version 1
*/
public class Generic {
public static void main(String[] args){
//jdk 1.5中几个类在定义集合时,明确比哦啊是你要集合中装那种类型的数据,无法加入指定类型以外的数据。
ArrayList collection = new ArrayList();
collection.add(1);
collection.add(1L);
collection.add("abc");
ArrayList<Integer> collection1 = new ArrayList<Integer>();
collection1.add(1);
// collection.add(1L);
// collection.add("abc");
//泛型是提供给javac编译器使用的,可以限定集合中的输入类型,让编译器挡住源程序中的非法输入,
//编译器编译带类型说明的集合时会去除“类型”信息,是程序允许那个效率不受影响,对于参数化的泛型类型,getClass()方法的返回值和原始类型完全一样。
//由于编译生成的字节码会去掉泛型的类型信息,只要能掉过编译器,就可以往某个泛型集合中加入其它类型数据,例如,用反射得到集合,再调用其add方法即可。
//参数化类型与原始类型的兼容性
Collection<String> c = new Vector();//参数化类型可以引用一个原始类型的对象,编译报告警告
Collection c1 = new Vector<String>();//原始类型可以应用给一个参数化类型的对象,编译警告
//参数化类型不考虑类型参数的继承关系:
// Vector<String> v = new Vector<Object>();//
错误!
// Vector<Object> v1 = new Vecotr<String>;//错误
// Vector v2 = new Vector<String>();
/*限定
通配符的上边界
* Vector<? extends Number> x = new Vector<Integer>();正确
* Vector<? extends Number> x = new Vector<String>(); 错误
*
* 限定通配符的下边界:
* Vector<? super Integer> x = new Vector<Number>();正确
* Vector<? super Integer> x = new Vector<Byte>();错误
*
* 提示: 限定通配符总是包括自己。
*/
HashMap<String,Integer> maps = new HashMap<String, Integer>();
maps.put("zhangsan", 22);
maps.put("lis", 23);
maps.put("wangwu", 28);
Set<Map.Entry<String, Integer>> entrySet = maps.entrySet();
for(Map.Entry<String, Integer> entry : entrySet){
System.out.println(entry.getKey()+" :: "+entry.getValue());
}
/*在jsp页面中哽咽经常要对Set或Map集合进行迭代
* <c:forEach items="${map}" var="entry">
* ${entry.key}:${entry.value}
* </c:forEach>
*/
/**用于放置泛型的类型参数的尖括号应出现在方法的其他所有修饰符之后和在方法的返回类型之前,就是紧邻返回值之前。
* 类型参数通常用单个大写字母白哦是。
* 只有引用类型才能作为泛型方法的实际参数,swap(new int[3],3,4);语句报告编译错误
* 除了在应用泛型是可以使用extends限定负,在定义泛型时也可以使用extends限定符。
* Class.get
Annotation()方法的定义,并且可以用&来制定多个边界,如<V extends Serializable & cloneable> void method(){}
*
* 普通方法、构造方法和
静态方法中都可以使用泛型。编译器也不允许穿件类型变量的数组。
* 也可以用类型变量表示
异常,称为参数化的遗产个,可以用于方法的throws列表中个,但是不能用于catch 字句中。
*
* 在泛型中更可以同时有多个类型参数,在定义
他们的尖括弧中用逗号分,
* 如public static <K,V> v getValue(K key){return map.get(key);}
*/
/*类型参数的类型推断
* 编译器判断泛型方法的实际类型参数的过程称为类型推断,类型推断是相对于
直觉推断的,其实现方法是一种非常复杂的过程。
* 根据调用泛型方法时实际传递的参数类型或返回值得类型来推断,具体 规则如下:
* 当某个类型变量只在整个参数李彪中的所有参数和返回值中的一处被应用,那么根据调用方法时该出的
实际应用类型来确定,这
* 很容易凭着感觉凭着感觉推断出来,即直接根据调用方法时传递的参数类型或返回值来决定泛型参数的类型,
* 如:swap(new String[3],3,4)-->static <E> void swap(E[] a,int i,int j)
* 当某个类型变量在整个参数类中的所有参数和返回值中的多出被应用了,如果调用方法时这多处的实际应用类型都对应同一个种
* 类型都对应同一种类型来确定,这很容易凭着感觉推断出来,
* 如:add(3,5) -->static <T> T add(T a,T b)
* 当某个类型变量在整个参数列表中的所有参数和返回值中的多处被应用了,如果调用方法时这多处的实际应用类型对应到了不同的类型,
* 且没有使用返回值,这时候取多个参数中的最大交集类型,
* 下面语句实际对应的类型是Number,编译没问题,只是运行时出问题:
* 如: fill(new Integer[3],3,5f) -->static <T> void fill(T[] a,T v)
* 当某个类型变量在整个参数列表中的所有参数和返回值中的多处被应用了,如果调用方法时这多处的实际应用类型对应到了不同的类型,
* 并且使用返回值,这时候优先考虑返回值的类型,
*
*/
// copy1(new Vector<String>(),new String[10]);
// copy2(new Date[10],new String[10]);
}
private static <T> void fillArray(T[] a,T obj){
for(int i=0;i<a.length;i++){
a[i] = obj;
}
}
private static <T> T autoConbert(Object obj){
return (T)obj;
}
//交换数组的两个元素的位置
static <E> void swap(E[] a,int i,int j){
E t = a[i];
a[i] = a[j];
a[j] = t;
}
public static void printCollection(Collection<Object> cols){
for(Object obj : cols){
System.out.println(obj);
}
//cols.add("String");没错
// cols = new HashSet<Date>();//运行错误
cols.size();//没错的,此方法与类型参数没有关系
/*
* 总结:使用?通配符可以引用其他各种参数化得类型,?通配符定义的变量主要用作引用,
* 可以调用与参数化无关的方法,不能调用与参数化有关的方法。
*/
}
}
2.
枚举
/**
* Enum
* 枚举:是一个类,每一个类都是一个对象。
* 就是要让某个类型的变量的取值只能为若干个固定值中的一个,负责,编译器就会报错。
* 枚举可以让编译器在编译时就可以控制源程序中填写的非法值,普通变量的方式在开发阶段无法实现这一目标
* @author xinglefly
* @version 1
*/
public abstract class WeekDay {
private WeekDay(){}
/* public final static WeekDay SUN = new WeekDay();
public final static WeekDay MON = new WeekDay();
*/ public final static WeekDay FRI = new WeekDay(){
public WeekDay nextDay1(){
return MON;
}
};
public final static WeekDay SUN = new WeekDay(){
public WeekDay nextDay1(){
return MON;
}
};
public final static WeekDay MON = new WeekDay(){
@Override
public WeekDay nextDay1() {
// TODO Auto-generated method stub
return SUN;
}
};
/*public WeekDay nextDay(){
if(this==SUN){
return MON;
}else{
return SUN;
}
}*/
public abstract WeekDay nextDay1();
public String toString(){
return this==SUN?"SUN":"MON";
}
}
public class EnumTest{
public static void main(String[] args){
WeekDay week = WeekDay.MON;
System.out.println(week.nextDay1());
WeekDay1 weekday = WeekDay1.FRI;
System.out.println(weekday);
System.out.println(weekday.name());
System.out.println(weekday.ordinal());
System.out.println(WeekDay1.valueOf("SUN").toString());
System.out.println(WeekDay1.values().length);
TraficLamp lamp = TraficLamp.RED;
System.out.println(lamp);
}
public enum WeekDay1{
SUN(1),MON(),TUE(),WED(),THI(),FRI(),SAT();
private WeekDay1(){System.out.println("first");}
private WeekDay1(int day){System.out.println("second");}
}
//枚举只有一个成员时,就可以作为一种单列的实现方式。
public enum TraficLamp{
RED(30){
public TraficLamp nextLamp(){
return GREEN;
}
},GREEN(45){
public TraficLamp nextLamp(){
return YELLOW;
}
},YELLOW(5){
public TraficLamp nextLamp(){
return RED;
}
};
public abstract TraficLamp nextLamp();
private int time;
TraficLamp(int time){
this.time=time;
}
};
}
反射
import java.lang.reflect.*;
import java.util.Arrays;
/**
* Reflect反射
* 反射就是把java类中的各种成分映射成相应的javav类。
* java程序中的各个java类属于同一类事物,描述这类事物的java类名就calss
* java类用于描述一类事物的共性,该类事物有什么属性,没什么属性,至于这个属性的值是什么,
* 则是由这个类的实例兑现过来确定的。不同的对象实例对象有不同的属性值。
* 一个类被类加载器加载到
内存中,占用一片存储空间,这个空间里面的内容就是类的字节码,
* 不同类的字节码是不同的,所以他们在内存中的内容是不同的。这一个个的空间可分别用一个个的对象来表示,这些对象显然具有相同的类型。
* 表示java类的class类显然要提供一系列的方法,来获得其中的变量,方法,构造方法,修饰符,包等信息。这些信息就是用形影类的实例对象来表示,
* 它们是Field、Method、Construtor、Package等等。
* @author xinglefly
* @version 1
*/
public class ReflectTest {
public static void main(String[] args)throws Exception{
/*各个字节码对应的实例对象Class类型。
* 1.类名.class ,System.class
* 2.对象.getClass(),如,new Date().getClass()
* 3.Class.forName("类名"), 如,Class.forName("java.util.Date");
*
* 九个预定义Class实例对象。(8个基本类型+void.class())
* int.class==Integer.TYPE
*
*/
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(cls1==cls3);
System.out.println(cls1.isPrimitive());//指定是否是一个基本类型
System.out.println(int.class.isPrimitive());
System.out.println(int.class==Integer.class);
System.out.println(int.class==Integer.TYPE);
System.out.println(int[].class.isPrimitive());
System.out.println(int[].class.isArray());
//Constructor类代表某个类中的一个构造方法
//得到某个类的所有的构造方法。
Constructor[] constructor = Class.forName("java.lang.String").getConstructors();
//得到某一个构造方法
Constructor constru = Class.forName("java.lang.String").getConstructor(StringBuffer.class);//获得方法时用到类型。
//创建实例对象
//通常方式
String str = new String(new StringBuffer("abc"));
//反射方式
String str3 = (String)constru.newInstance(new StringBuffer("abc"));
Constructor con1 = String.class.getConstructor(StringBuffer.class);
//Class.newInstance()方法:
//该方法内部先得到默认的构造方法,然后用该构造方法创建实例对象。
String str2 = (String)con1.newInstance(new StringBuffer("abc"));
System.out.println(str2.charAt(2));
ReflectPoint pt1 = new ReflectPoint(3,5);
Field fieldY = pt1.getClass().getField("y");
//fieldY的值是多少?是5,错!field不是对象身上的变量,而是类上,
System.out.println(fieldY.get(pt1));
//对私有属性
Field fieldX = pt1.getClass().getDeclaredField("x");
fieldX.setAccessible(true);
System.out.println(fieldX.get(pt1));
changeStringValue(pt1);
System.out.println(pt1);
/*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对象对应的是一个静态方法!
// new String[]{new String("abc"),new String("xyz"),new String("123")};
Method methodCharAt = String.class.getMethod("charAt", int.class);
System.out.println(methodCharAt.invoke(str1, 1));
System.out.println(methodCharAt.invoke(str1, new Object[]{2}));
String startingClassName = args[0];
Method mainMethod = Class.forName(startingClassName).getMethod("main",String[].class);
mainMethod.invoke(null, new Object[]{new String[]{"111","222","333"}});
/*数组反射
* 具有相同维数和元素类型的数组属于同一个类型,即具有相同的class实例对象。
* 代表数组的Class实例对象的getSuperClass()方法返回的父类为Object类对应的class
* 基本类型的一维数组可以被当做Object类型使用,不能当做object[]类型使用;
* 非基本类型的一维数组,即可以当做Ojbect[]类型使用,又可以当做Object[]类型使用。
*/
int [] a1 = new int[]{1,2,3};
int [] a2 = new int[4];
int [][] a3 = new int[2][3];
String[] a4 = new String[]{"a","b","c"};
sop(a1.getClass() == a2.getClass());
// sop(a1.getClass() == a4.getClass());
// sop(a1.getClass() == a3.getClass());
sop(a1.getClass().getName());
sop(a1.getClass().getSuperclass().getName());
sop(a4.getClass().getSuperclass().getName());
sop(Arrays.asList(a1));
sop(Arrays.asList(a4));
printObject(a4);
printObject("xyz");
}
public static void printObject(Object obj){
Class clazz = obj.getClass();
if(clazz.isArray()){
int len = Array.getLength(obj);
for(int i=0;i<len;i++){
sop(Array.get(obj, i));
}
}else{
sop(obj);
}
}
//替换文件中的字节
private static void changeStringValue(Object obj)throws Exception{
Field[] fields = obj.getClass().getFields();
System.out.println(fields);
for(Field field : fields){
//因为是同一个字节码的比较用==
//if(field.getType().equals(String.class)){
if(field.getType() == String.class){
String oldValue = (String)field.get(obj);
String newValue = oldValue.replace('b', 'a');
field.set(obj, newValue);
}
}
}
public static void sop(Object obj){
System.out.println(obj);
}
}
class TestArguments{
public static void main(String[] args){
for(String arg : args){
System.out.println(arg);
}
}
}
JavaBean
**
* JavaBean 是一种特殊的java类,主要用于
传递数据信息,这种java类中的主方法主要用于访问私有的字段,且方法名符合某种命名规则。
* 如果要在练个模块之间出点多个信息,可以将这些信息封装到一个javabean中,这种JavaBean的实例对象通常称为值对象(Value-Object,简称VO)
* 这些信息在类中用私有字段来存储,如果读取或设置这些字段的值,则需要通过一些相应的方法来访问。
*
* 总之,一个类被当做javaBean使用时,javaBean的属性是根据方法名推断处出来的,它根本看不到java类内部的成员变量。
* @author xinglefly
* @version 1
*/
public class IntroSpectorTest {
/* 一个符合javabean特点的类可以当做普通类一样进行使用个,但它当javabean用肯定需要带来一些额外的好处,我们才会去了解和应用javabean!
* 好处: 在java EE开发中,经常要使用到javabean.很多环境就要求按JavaBean方式进行操作,别人都这么用和要求这么做,那你就没什么挑选的余地。
* JDK中提供了对javabean进行操作的一些API,这套API就称为内省。如果要你自己去通过getX方法来访问私有的X,用省这套api操作JavaBean比用普通类的方式更方便。
*/
public static void main(String[] args)throws Exception{
ReflectPoint pt1 = new ReflectPoint(3,5);
String propertyName = "x";
//"x"-->"X"---"getX"-->MethodGetX-->
PropertyDescriptor pd = new PropertyDescriptor(propertyName,pt1.getClass());
Method methodGetX = pd.getReadMethod();
Object retVal = methodGetX.invoke(pt1);
System.out.println(retVal);
/*Method methodSetX = pd.getWriteMethod();
methodSetX.invoke(pt1, 7);
System.out.println(pt1.getX());
*/
Object value = 8;
setProperties(pt1,propertyName,value);
System.out.println(getProperty(pt1,propertyName));
/*JDK 1.7的新特性
* Map map = (name:"zxx",age:18)
* BeanUtils.setProperty(map,"name","1hm");
*/
//出错
BeanUtils.setProperty(pt1, "birthday.time", "111");
System.out.println(BeanUtils.getProperty(pt1, "birthday.time"));
// PropertyUtils.setProperty(pt1, "x", 9);
}
public static void setProperties(Object pt1,String propertyName,Object value)throws Exception{
PropertyDescriptor pd2 = new PropertyDescriptor(propertyName,pt1.getClass());
Method methodSetX = pd2.getWriteMethod();
methodSetX.invoke(pt1, value);
}
/*采用遍历BeanInfo的所有属性方式来查找和设置某个RefectPoint对象的X属性。在程序中把一个类当做
* JavaBean来看,就是调用IntroSpector.getBeanInfo方法,得到的BeanInfo对象封装了把这个类当做JavaBean看的结果信息。
*
*/
private static Object getProperty(Object pt1,String propertyName)throws Exception{
/*PropertyDescriptor pd = new PropertyDescriptor(propertyName,pt1.getClass());
Method methodGetX = pd.getReadMethod();
Object retVal = methodGetX.invoke(pt1);*/
BeanInfo beaninfo = Introspector.getBeanInfo(pt1.getClass());
PropertyDescriptor[] pds = beaninfo.getPropertyDescriptors();
Object retVal = null;
for(PropertyDescriptor pd : pds){
if(pd.getName().equals(propertyName)){
Method methodGetX = pd.getReadMethod();
retVal = methodGetX.invoke(pt1);
break;
}
}
return retVal;
}
}
5.加载器
用eclipse的打包工具将ClassLoaderTest输出成jre/lib/ext目录下的itcast.jar包,再在eclipse中运行这个类,运行结果显示为ExtClassLoader.此时的环境状态是classpath目录有ClassLoaderTest.class,ext/itcast.jar包中也有ClassLoaderTest.class,这时候我们就需要了解加载的具体过程和原理了。
类加载器的委托机制
当Java虚拟机要加载一个类时,到底派出那个类加载器去加载呢?
首先当前
线程的类加载器去加载线程中的第一个类。
如果类A中引用了类B,java虚拟机将使用加载类的A的类加载器来加载类B.
还可以直接调用ClassLoader.loadClass()方法来指定某个类加载器去加载某个类。
每个类加载器加载时,又先委托给其上级类加载器。
当所有祖宗类加载器没有加载到类,回到发起者类加载器,还加载不了,则抛ClassNotFountdException,不是再去找发起者类加载器的儿子,因为没有getChild方法,即使有,那么多儿子,找哪一个呢?
对着类加载器的参差结构图和
委托加载原理,解释先前将ClassLoaderTest输出成jre/lib/ext目录下的itcast.jar包中后,运行结果为ExtClassLoader的原因。
编写自己的类加载器
自定义的类加载器的必须继承ClassLoader
loadClass方法与findClass方法
defineClass方法
编程步骤:
编写一个对文件内容进行简单加密的程序。
编写了一个自己的类装载器,可实现对加密过的类进行装载和解密。
编写一个程序调用类加载器加载类,在源程序中不能用该类名定义引用变量,因为编译器无法识别这个类。程序中可以除了使用ClassLoader.load方法之外,还可以使用设置线程的上下文类加载器或者系统类加载器,然后再使用Class.forName。
实验步骤:
对不带包名的class文件进行加密,加密结果存放到另外一个目录,例如: java MyClassLoader MyTest.class F:\itcast
运行加载类的程序,结果能够被正常加载,但打印出来的类装载器名称为AppClassLoader:java MyClassLoader MyTest F:\itcast
用加密后的类文件替换CLASSPATH环境下的类文件,再执行上一步操作就出问题了,错误说明是AppClassLoader类装载器装载失败。
删除CLASSPATH环境下的类文件,再执行上一步操作就没问题了。
代理的概念与作用
程序中的代理
要为已存在的多个具有相同
接口的目标类的各个方法增加一些系统功能,例如,
异常处理、日志、计算方法的运行时间、事务管理、等等,你准备如何做?
编写一个与目标类具有相同接口的代理类,代理类的每个方法调用目标类的相同方法,并在调用方法时加上系统功能的代码。 (参看下页的原理图)
如果采用工厂模式和配置文件的方式进行管理,则不需要修改客户端程序,在配置文件中配置是使用目标类、还是代理类,这样以后很容易切换,譬如,想要日志功能时就配置代理类,否则配置目标类,这样,增加系统功能很容易,以后运行一段时间后,又想去掉系统功能也很容易。
动态代理技术
要为系统中的各种接口的类增加代理功能,那将需要太多的代理类,全部采用静态代理方式,将是一件非常麻烦的事情!写成百上千个代理类,是不是太累!
JVM可以在运行期动态生成出类的字节码,这种动态生成的类往往被用作代理类,即动态代理类。
JVM生成的动态类必须实现一个或多个接口,所以,JVM生成的动态类只能用作具有相同接口的目标类的代理。
CGLIB库可以动态生成一个类的子类,一个类的子类也可以用作该类的代理,所以,如果要为一个没有实现接口的类生成动态代理类,那么可以使用CGLIB库。
代理类的各个方法中通常除了要调用目标的相应方法和对外返回目标返回的结果外,还可以在代理方法中的如下四个位置加上系统
功能代码:
1.在调用目标方法之前
2.在调用目标方法之后
3.在调用目标方法前后
4.在处理目标方法异常的catch块中