黑马程序员-java基础强化(二)

论坛 期权论坛 脚本     
匿名技术用户   2020-12-28 20:56   461   0

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

体验泛型

1. jdk1.5以前集合类中存在着编译要强制转换且运行时出错的问题...

2. jdk1.5的集合类希望你在定义集合时,明确表示你要向集合中装那种类型的数据(不是必须的),无法加入指定类型以外的数据

3. 泛型是提供给javac编译器是用的,可以限定集合中的输入类型,让编译器挡住源程序中的非法输入,编译器编译带类型说明的集合时会去掉”类型”信息,使程序运行效率不受影响,对于参数化得泛型类型,getClass方法的返回值和原始类型完全一样。由于编译生成的字节码会去掉泛型类型信息,只要能跳过编译器,就可以往某个泛型集合中加入其它类型数据,例如,用反射得到集合,在调用add方法即可。

了解泛型

1. ArrayList<E>类定义和ArrayList<Integer>类引用中设计如下术语:

a) 整个称为ArrayList<E>泛型类型

b) ArrayList<E>中的E称为类型变量或类型参数

c) 整个ArrayList<Integer>称为参数化的类型

d) ArrayList<Integer>中的Integer称为类型参数的实例或实际类型参数

e) ArrayList<Integer>中的<>念作typeof

f) ArrayList称为原始类型

2. 参数化类型与原始类型的兼容性:

a) 参数化类型可以引用一个原始类型的对象,编译报告警告,例如

Collection<String>c = new Vector();

b) 原始类型可以引用一个参数化类型的对象,编译报告警告,例如

Collection c = newVector<String>();

//原来的方法接受一个集合参数,ind类型也要能传进去

3. 参数化类型不考虑类型参数的继承关系:

a) Vector<String> v = new Vector<Object>();

//错误//不写Object没错,写了就是明知故犯

b) Vector< bject> v = new Vector<String>();

//也错!!

4. 在创建数组实例时,数组的元素不能使用参数化的类型,例如,下面语句有错误:

Vector<Integer>vectorList[] = new Vector<Intger>[10];

//记住是错误的就行

5. 下面代码会报错么?不会,java编译器是按行执行的,只要行代码没错,就不会报错

Vector v1 = newVector<String>();

Vector<Object>= v1;

泛型中的?通配符

问题:

定义一个方法,该方法用于打印出任意参数化类型的集合中的所有数据,该方法如何定义?不能用于具体的参数操作,因为不确定传入的是什么参数,但是想size()方法可以,因为所有的都有这个方法

总结使用?通配符可以引用其他各种参数化的类型,?通配符定义的变量主要用于做引用,可以调用与参数化无关的方法,不能调用与参数化有关的方法

泛型中的?通配符的扩展

1. 限定通配符的上边界:

正确:Vector<? extendsNumber> x = new Vector<Integer>();

错误:Vector<? extendsNumber> x = new Vector<String>();

2. 限定通配符的下边界:

正确:Vector<? superInteger> x = new Vector<Number>();

错误:Vector<? superInteger> x = new Vector<Byte>();

注:限定通配符总是包括自己。

定义泛型方法

1. 用于放置泛型的类型参数的尖括号应出现的其他所有修饰符之后和在方法的返回类型之前,也就是紧邻返回值之前。按照惯例,类型参数通常用单个大写字母表示。

2. 只用引用类型才能作为泛型方法的实际参数,基本类型不行...

3. 除了在应用泛型时可以使用extends限定符,在定义泛型时也可以使用extends限定符,例如,Class.getAnnotation()方法的定义。并且可以用&来指导多个边界,如<Vextends Serializable & cloneable> void method(){}

4. 普通方法、构造方法和静态方法中都可以使用泛型。

5. 也可以用类型变量表示异常,称为参数化的异常,可以用于方法的throws列表中,但是不能用于catch子句中。

6. 在泛型中可以同时有多个类型参数,在定义它们的尖括号中用逗号分,例如:publicstatic <K,V> V getValue(K key){return map.get(key);}

定义泛型类型:

1. 如果类的实例对象中的多处都要用到同一个泛型参数,即这些地方引用的泛型类型要保持同一个实际类型时,这时候就要采用泛型类型的方式定义,也就是类级别的泛型,多用于crud,语法如下:

public class GenericDao<T> {

publicvoid add(T x){}

publicT findById(int id){

returnnull;

}

2. 类级别的泛型是根据引用该类名时指定的类型信息来参数化类型变量的,例如,如下两种方式都可以:

a) GenericDao<String> dao = null;

b) New genericDao<String>();

3. 注意:

a) 在对泛型类型进行参数化时,类型参数的实例必须是引用类型,不能是基本类型

b) 当一个变量被声明为泛型时,只能被实例变量和方法调用(还有内嵌类型),而不能被静态变量和静态方法调用。因为静态成员是被所有参数化的类所共享的,所以静态方法不应该有类级别的类型参数。

4. 问题:类中只有一个方法需要使用泛型,是使用类级别的泛型,还是使用方法级别的泛型?使用方法级别的即可

通过反射获得泛型的参数化类型

一点点往上倒...

GenericTest类:

public class GenericTest {

/**

* @param args

*/

public static void main(String[] args) throws Exception{

// TODO Auto-generatedmethod stub

ArrayList collection1 = new ArrayList();

collection1.add(1);

collection1.add(1L);

collection1.add("abc");

//int i = (Integer)collection1.get(1);

ArrayList<String> collection2 = newArrayList<String>();

//collection2.add(1);

//collection2.add(1L);

collection2.add("abc");

String element = collection2.get(0);

System.out.println(element);

Constructor<String> constructor1 = String.class.getConstructor(StringBuffer.class);

String str2 = (String)constructor1.newInstance(/*abc*/new StringBuffer("abc"));//类型需要强制转换

System.out.println(str2.charAt(2));

ArrayList<Integer> collection3 = newArrayList<Integer>();

System.out.println(collection3.getClass() ==collection2.getClass());

collection3.getClass().getMethod("add", Object.class).invoke(collection3,"hello");

System.out.println(collection3.get(0));

printCollection(collection3);

HashMap<String, Integer> maps = new HashMap<String,Integer>();

maps.put("zxx", 12);

maps.put("lhm", 22);

maps.put("flx", 33);

Set<Map.Entry<String, Integer>> entrySet =maps.entrySet();

for(Map.Entry<String, Integer> entry : entrySet){

System.out.println(entry.getKey() + ":" + entry.getValue());

}

add(3,5);

Number x1 = add(3.5,3);

Object x2 = add(3, "abc");

swap(new String[]{"abc","xyz","hello"},1,2);

//swap(new int[]{3,4,1},1,2);

//基本类型不能被泛型方法调用...上一个例子可以,因为jdk1.5拥有自动装箱功能,将int封装成了Integer

Object obj = "abv";

String x3 = autoConvert(obj);

GenericDao<ReflectPoint> dao = newGenericDao<ReflectPoint>();

dao.add(new ReflectPoint(5, 6));

//String s = dao.findById(1);

Method applyMethod = GenericTest.class.getMethod("applyVector", Vector.class);

Type[] types = applyMethod.getGenericParameterTypes();

ParameterizedTypepType = (ParameterizedType)types[0];

System.out.println(pType.getRawType());

System.out.println(pType.getActualTypeArguments()[0]);

}

public static void applyVector(Vector<Date> v){

}

private static <T> T autoConvert(Object obj) {

// TODO Auto-generatedmethod stub

return (T)obj;

}

private static <T> void swap(T[] a,int i,int j){

T tmp = a[i];

a[i] = a[j];

a[j] = tmp;

}

private static <T> T add(T x, T y) {

return null;

}

private static void printCollection(Collection<?> collection) {

// TODO Auto-generatedmethod stub

for(Object obj : collection){

System.out.println(obj);

}

}

}

GenericDao类

public class GenericDao<T> {

//dao data access object --->crud

public void add(T x){

}

public T findById(int id){

return null;

}

public void delete(T obj){

}

public void delete(int id){

}

public void update (T obj){

}

public static <T> void update2(T obj){

}

public T findByUserName(String name){

return null;

}

public Set<T> findByConditions(String where){

return null;

}

}

类加载器:

1. java虚拟机中可以安装多个加载器,系统默认三个主要类加载器,每个类负责加载特定位置的类:BootStrap,ExtClassLoader,AppClassLoader

2. 类加载器也是java类,因为其他是java类的类加载器本身也要被类加载器加载,显然必须有第一个类加载器不是不是java类,这正是BootStrap。

3. java虚拟机中的所有类加载器采用具有父子关系的树形结构进行组织,在实例化每个类装载其对象时,需要为其指定一个父级类装载器对象或者默认采用系统类装载器为其父级类加载

类加载器之间的父子关系和管辖范围图:

类加载器的委托机制

1. 当java虚拟机要加载一个类时,到底派出那个类加载器去加载呢?

a) 首先当前线程的类加载器去加载线程中的第一个类。

b) 如果类A中引用了类B,java虚拟机将使用加载类A的类装载器来加载类B

c) 还可以之间调用ClassLoader.loadClass()方法来指定某个类加载器去加载某个类。

2. 每个类加载器加载类时,又先委托给其上级类加载器。

a) 当所有祖宗类加载器没有加载到类,回到发起者类加载器,还加载不了,则抛出ClassNotFoundException,不会再去找发起者类加载器的儿子,因为没有getChild方法,即使有,那有多个儿子,找哪一个呢?所以不会再找了

b) 对着类加载器的层次结构图和委托加载原理解释先前将ClassLoaderTest输出成jre/lib/ext目录下itcast.jar包中后,运行结果为ExtClassLoader的原因:ext是app的爸爸,他优先找,并找到了

ClassLoaderTest类:

public class ClassLoaderTest {

public static void main(String args[]) throws Exception{

System.out.println(

ClassLoaderTest.class.getClassLoader().getClass().getName());

System.out.println(

System.class.getClassLoader());

ClassLoader loader = ClassLoaderTest.class.getClassLoader();

while(loader != null){

System.out.println(loader.getClass().getName());

loader = loader.getParent();

}

System.out.println(loader);

//System.out.println(newClassLoaderAtachment().toString());

Class clazz = new MyClassLoader("itcastlib").loadClass("ClassLoaderAttachment");

Date d1 = (Date)clazz.newInstance();

System.out.println(d1);

}

}

ClassLoaderAtachment类:

public class ClassLoaderAtachment extends Date {

public String toSting(){

return "hello,lo";

}

}

代理的概念与作用:

1. 要为已存在的多个具有相同接口的目标类的各个方法增加一些系统功能,例如,异常处理、日志、计算方法的运行时间、事务管理、等等,你准备如何做?

2. 编写一个与目标类具有相同接口的代理类,代理类的每个方法调用目标类的相同方法,并在调用方法是加上系统功能的代码

3. 如果采用工厂模式和配置文件的方式进行管理,则不需要修改客户端程序,在配置文件中配置是使用目标类、还是代理类,这样以后很容易切换,譬如,想要日志功能时就配置代理类,否则配置目标类,这样,增加系统功能很容易,以后运行一段时间后,又想去掉系统功能也很容易。

AOP

1. 系统中存在交叉业务,一个交叉业务就是要切入到系统中的一个方面,如下所示:

安全 事务 日志

StudentService -----|-----------|-------------|------

CourseService -----|-----------|-------------|------

MiscService -----|-----------|-------------|------

2. 用具体的程序代码描述交叉业务:

method1 method2 method3

{ { {

------------------------------------------------切面

... ... ...

------------------------------------------------切面

} } }

3. 交叉业务的编程问题即为面向方面的编程(Aspect orientedprogram,简称AOP),AOP的目标就是要使交叉业务模块化。可以采用将切面代码移动到原始方法的周围,这与之间在方法中编写切面代码的运行效果是一样的,如下图

------------------------------------------------切面

func1 func 2 func3

{ { {

... ... ...

} } }

------------------------------------------------切面

4. 使用代理技术正好可以解决这种问题,代理是实现AOP功能的核心和关键技术。

动态代理技术

1. 要为系统中的各种接口的类增加代理功能,那将需要太多的代理类,全部采用静态代理方式,将是一件非常麻烦的事情!写成百上千个代理类,是不是太累了!

2. JVM可以在运行期动态生成出类的字节码。这种动态生成的类往往被用作代理类,即动态代理类

3. JVM生成的动态类必须实现一个或多个接口,所以,JVM生成的动态类只能用作具有相同接口的目标类的代理。

4. CGLIB库可以动态生成一个类的子类,一个类的子类也可以用作该类的代理,所以,如果要为一个没有实现接口的类生成动态代理类,那么可以使用CGLIB库。

5. 代理类的各个方法中通常除了要调用目标的相应方法和对外返回目标返回的结果外,还可以在代理方法中的如下四个位置加上系统功能代码:

a) 在调用目标方法之前

b) 在调用目标方法之后

c) 在调用目标方法前后

d) 在处理目标方法异常的catch块中

例:Class proxy{

voidsayHello(){

.......

try{

target.sayHello();

}catch(Exceptione){

.......

}

.......

}

}

分析JVM动态生成的类

1. 创建实现了Collection接口的动态类和查看其名称,分析Proxy.getProxyClass方法的各个参数。

2. 编码列出动态类中的所有构造方法和参数签名

3. 编码列出动态类中的所有方法和参数签名

4. 创建动态类的实例对象

a) 用反射获得构造方法

b) 编写一个最简单的InvocationHandler类

c) 调用构造方法创建动态类的实例对象,并将编写的InvocationHandler类的实例对象传进去

d) 打印创建的对象和调用对象的没有返回值的方法和getClass方法,演示调用其他有返回值的方法报告了异常。

e) 将创建动态类的实例对象的代理改成匿名内部类的形式编写,锻炼大家习惯匿名内部类。

5. 思考总结:让jvm创建动态类及其实例对象,需要给他提供哪些信息?

a) 三个方面

i. 生成的类中有哪些方法,通过让其实现那些接口的方式进行告知;

ii. 产生的类字节码必须有一个关联的类加载器对象;

iii. 生成的类中的方法的代码是怎样的,也得有我们提供。把我们的代码写在一个约定好了接口对象的方法中,把对象传给它,它调用我的方法,即相当于插入了我的代码。提供执行代码的对象就是那个InvocationHandler对象,它是在创建动态类的实例对象的构造方法时传递进去的。在上面的InvocationHandler对象的invoke方法中加一点代码,就可以看到这些代码被调用运行了。

6. 用Proxy.newInstance方法直接一步就创建出代理对象

     Foo f = (Foo) Proxy.newProxyInstance(Foo.class.getClassLoader(),
                                          new Class[] { Foo.class },
                                          handler);

猜想分析动态生成的类的内部代码

1. 动态生成的类实现了Collection接口(可以实现若干接口),生成的类有Collection接口中的所有方法和一个如下接受InvocationHandler参数的构造方法。

2. 构造方法接受一个InvocationHandler对象,接收对象了要干什么用呢?该方法内部的代码会是怎样的呢?

3. 实现Collection接口的动态类中的各个方法的代码又是怎样的呢?InvocationHandler接口中定义invoke方法接受的三个参数又是什么意思?图解说明如下“

Client程序调用objProxy.add(“abc”)方法时,

涉及三要素:objProxy对象、add方法、”abc”参数

Class Proxy${

add(Object object){

return handler.invoke(Object proxy,Methodmethod,Object args);

}

}

4. 分析Proxy1的打印动态类的实例对象时,结果为什么会是null呢?

因为返回值是null

让动态生成的类成为目标类的代理

1. 动态代理的工作原理图

2. 怎样将目标类传进去?

a) 直接在InvocationHandler实现类中创建目标类的实例对象,可以看运行效果和加入日志代码,但没有实际意义。

b) 为InvocationHandler实现类注入目标类的实例对象,不能采用匿名内部类的形式了

c) 让匿名的InvocationHandler实现类访问外面方法中的目标类实例对象的final类型的引用变量

3. 将创建代理的过程改为一种更优雅的方式,eclipse重构出一个getProxy方法绑定接收目标懂事返回代理对象,让调用者更方便,调用者甚至不用接触任何代理API

4. 将系统功能代码模块化,即将切面代码也改为通过参数形式的提供,怎样把要执行的系统功能代码以参数形式提供?

a) 把要执行的代码装到一个对象的某个方法里,然后把这个对象作为参数传递,接收者只要调用这个对象的方法,即等于执行了外界提供的代码!

b) 为bind方法增加一个Advice参数

ProxyTest类:

public class ProxyTest {

/**

* @param args

*/

public static void main(String[] args) throws Exception{

// TODO Auto-generatedmethod stub

Class clazzProxy1 = Proxy.getProxyClass(Collection.class.getClassLoader(),Collection.class);

System.out.println(clazzProxy1.getName());

System.out.println("---------beginconstructors list----------");

//$Proxy0()

//$Proxy0(InvocationHandler,int)

Constructor[] constructors =clazzProxy1.getConstructors();

for(Constructor constructor : constructors){

String name = constructor.getName();

StringBuilder sBuilder = new StringBuilder(name);

sBuilder.append('(');

Class[] clazzParams =constructor.getParameterTypes();

for(Class clazzParam : clazzParams){

sBuilder.append(clazzParam.getName()).append(',');

}

if(clazzParams != null && clazzParams.length !=0){

sBuilder.deleteCharAt(sBuilder.length()-1);

}

sBuilder.append(')');

System.out.println(sBuilder.toString());

}

System.out.println("---------beginmethods list----------");

Method[] methods = clazzProxy1.getMethods();

for(Method method : methods){

String name = method.getName();

StringBuilder sBuilder = new StringBuilder(name);

sBuilder.append('(');

Class[] clazzParams = method.getParameterTypes();

for(Class clazzParam : clazzParams){

sBuilder.append(clazzParam.getName()).append(',');

}

if(clazzParams != null && clazzParams.length !=0){

sBuilder.deleteCharAt(sBuilder.length()-1);

}

sBuilder.append(')');

System.out.println(sBuilder.toString());

}

System.out.println("---------begincreat instance object----------");

//Object obj =clazzProxy1.newInstance();

Constructor constructor = clazzProxy1.getConstructor(InvocationHandler.class);

class MyInvocationHander1 implements InvocationHandler{

@Override

public Objectinvoke(Objectproxy, Method method, Object[]args)

throws Throwable {

// TODO Auto-generatedmethod stub

return null;

}

}

Collection proxy1 = (Collection)constructor.newInstance(newMyInvocationHander1());

System.out.println(proxy1);

proxy1.clear();

//proxy1.size();

Collection proxy2 = (Collection)constructor.newInstance(newInvocationHandler(){

@Override

public Objectinvoke(Objectproxy, Method method, Object[]args)

throws Throwable {

return null;

}

});

final ArrayList target = new ArrayList();

Collection proxy3 = (Collection) getProxy(target,new MyAdvice());

proxy3.add("hello");

proxy3.add("today");

proxy3.add("yesterday");

System.out.println(proxy3.size());

System.out.println(proxy3.getClass().getName());

}

private static ObjectgetProxy(final Objecttarget,final Advice advice) {

Objectproxy3 = Proxy.newProxyInstance(

target.getClass().getClassLoader(),

//Collection.class.getClassLoader(),

target.getClass().getInterfaces(),

new InvocationHandler(){

@Override

public Objectinvoke(Objectproxy, Method method, Object[]args)

throws Throwable {

/*long beginTime= System.currentTimeMillis();

Object retVal = method.invoke(target, args);

long endTime = System.currentTimeMillis();

System.out.println(method.getName() +":running time of " + (endTime - beginTime));

return retVal;*/

advice.beforeMethod(method);

Object retVal = method.invoke(target, args);

advice.afterMethod(method);

return retVal;

}

});

return proxy3;

}

}

Advice接口:

public interface Advice{

void beforeMethod(Method method);

void afterMethod(Method method);

}

MyAdvice类:

public class MyAdvice implements Advice {

long beginTime = 0;

public void beforeMethod(Method method) {

// TODO Auto-generatedmethod stub

System.out.println("hello");

beginTime = System.currentTimeMillis();

}

public void afterMethod(Method method) {

// TODO Auto-generatedmethod stub

long endTime = System.currentTimeMillis();

System.out.println(method.getName() + ":running time of " + (endTime- beginTime));

System.out.println("bye\n");

}

}

实现AOP功能的封装与配置

1. 工厂类BeanFactory负责创建目标类或代理类的实例对象,并通过配置文件实现切换。其getBean方法根据参数字符串返回一个相应的实例对象,如果参数字符串在配置文件中对应的类名不是ProxyFatoryBean,则直接返回该类的实例对象,否则,返回该类实例对象的getProxy方法的返回的对象

2. BeanFactory的构造方法接收代表配置文件的输入流对象,配置文件格式如下

#xxx=java.util.ArrayList

xxx=cn.itcast.day3.aopframework.ProxyFactoryBean

xxx.advice=cn.itcast.day3.MyAdvice

xxx.target=java.util.ArrayList

3. ProxyFacotryBean充当封装生产动态代理的工厂,需要为工厂类提供哪些配置参数信息?

a) 目标

b) 通知

BeanFactory类:

public class BeanFactory {

Properties props = new Properties();

public BeanFactory(InputStream ips){

try {

props.load(ips);

} catch (IOException e) {

// TODO Auto-generatedcatch block

e.printStackTrace();

}

}

public Object getBean(String name){

String className = props.getProperty(name);

Object bean = null;

Class clazz;

try {

clazz = Class.forName(className);

bean = clazz.newInstance();

} catch (Exception e) {

// TODO Auto-generatedcatch block

e.printStackTrace();

}

if(bean instanceof ProxyFactoryBean){

Object proxy = null;

ProxyFactoryBean proxyFactoryBean =(ProxyFactoryBean)bean;

try {

Advice advice = (Advice)Class.forName(props.getProperty(name +".advice")).newInstance();

Object target = Class.forName(props.getProperty(name +".target")).newInstance();

proxyFactoryBean.setAdvice(advice);

proxyFactoryBean.setTarget(target);

proxy = proxyFactoryBean.getProxy();

} catch (Exception e) {

// TODO Auto-generatedcatch block

e.printStackTrace();

}

return proxy;

}

return bean;

}

}

ProxyFactoryBean类:

public class ProxyFactoryBean {

private Object target;

private Advice advice;

public Object getTarget() {

return target;

}

public void setTarget(Object target) {

this.target = target;

}

public Object getAdvice() {

return advice;

}

public void setAdvice(Advice advice) {

this.advice = advice;

}

public Object getProxy(){

Object proxy3 = Proxy.newProxyInstance(

target.getClass().getClassLoader(),

//Collection.class.getClassLoader(),

target.getClass().getInterfaces(),

new InvocationHandler(){

@Override

public Object invoke(Object proxy, Method method, Object[]args)

throws Throwable {

/*long beginTime= System.currentTimeMillis();

Object retVal = method.invoke(target, args);

long endTime = System.currentTimeMillis();

System.out.println(method.getName() +":running time of " + (endTime - beginTime));

return retVal;*/

advice.beforeMethod(method);

Object retVal = method.invoke(target, args);

advice.afterMethod(method);

return retVal;

}

});

return proxy3;

}

}

AopFrameworkTest(测试类):

public class AopFrameworkTest {

/**

* @param args

*/

public static void main(String[] args) {

// TODO Auto-generatedmethod stub

InputStream ips = AopFrameworkTest.class.getResourceAsStream("config.properties");

Object bean = new BeanFactory(ips).getBean("xxx");

System.out.println(bean.getClass().getName());

((Collection)bean).clear();//示范

}

}

config.properties(配置文件):

#xxx=java.util.ArrayList

xxx=cn.itcast.day3.aopframework.ProxyFactoryBean

xxx.advice=cn.itcast.day3.MyAdvice

xxx.target=java.util.ArrayList

分享到 :
0 人收藏
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

积分:7942463
帖子:1588486
精华:0
期权论坛 期权论坛
发布
内容

下载期权论坛手机APP