黑马程序员_高薪技术(二)

来源:互联网 发布:mac怎么卸载adobe软件 编辑:程序博客网 时间:2024/05/26 14:08
---------------------- android培训 、 java培训 、 期待与您交流! ----------------------


18、Beanutils工具包
        把Beanutils的jar包添加到Buildpath
        如果在Beanutils工具包中使用到了别的工具包,在使用Beanutils工具包时,要把其使用的别的工具包也一起添加到Buildpath

19、 了解注解及java提供的几个基本注解
    
        @Deprecated
        @Override
        @SuppressWarnings
        @Retention(RetentionPolicy.RUNTIME)保留注解到运行状态

        总结:注解相当于一种标记,加了注解就等于打上了某种标记,没加,则等于没有某种标记,以后,javac编译器,
        开发工具和其他程序可以用反射来了解你的类及各种元素上有无何种标记,看你有什么标记,就去干相应的事。
        标记可以加在包,类,字段,方法的参数以及局部变量上。

20、 泛型
        使用了通配符"?",就不能再使用与类型有关的方法

        1. 定义泛型的方法
        
        2. 定义泛型的类型
        如果类的实例对象中多处都要用到同一个泛型参数,即这些地方引用的泛型类型都要保持同一个实际类型时,
        这时候就要采用泛型的方式进行定义,也就是类级别的泛型,语法格式如下:
        public class GenericDao<T>{
            private T field1;
            public void save(T obj){}
            public T getById(int id){}
        }

        类级别的泛型是根据引用该类名时指定的类型信息来参数化类型变量的,如下两种方式
            GenericDao<String>dao=null;
            new genericDao<String>();
        
        注意:
        在对泛型类型进行参数化时,类型参数的实例化必须是引用类型,不能是基本类型。
        当一个变量被声明为泛型时,只能被实例变量和方法调用(还有内嵌类型),而不能被静态变量和静态方法调用。
        因为静态成员是被所有参数化的类所共享的,所以静态成员不应该有类级别的类型参数。

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

<span style="font-size:18px;">public class GenericDao<E>{public void add(E x){}public E findById(int id){}public void delete(E obj){}public void delete(int id){}public void update(E obj){}public Set<E>findByConditions(String whete){return null;}public Set<E> findByUserName(String name){return null;}}</span>
21. 类加载器,就是加载类的工具
        Java虚拟机中有多个类加载器,系统默认三个主要类加载器,每个类负责加载特定位置的类
        BootStrap, ExtClassLoader, AppClassLoader

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

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

22. 类加载器的委托机制
        当Java虚拟机要加载一个类时,到底派出哪个类加载器去加载呢?
        首先当前线程类加载器去加载线程中的第一个类。
        如果类A中引用了类B,java虚拟机将使用加载类A的类装载器来加载类B
        还可以直接调用ClassLoader.loadClass()方法来指定某个类加载器去加载某个类。

23. 每个类加载器加载类时,又先委托给其上级类加载器
        当所有祖宗类加载器没有加载到类,回到发起者类加载器,还加载不了,则抛出ClassNoFoundException,
        不是再去找发起者类加载器的儿子,因为没有getChild方法,即使有,那有多个儿子,找哪个呢?

        对着类加载器的层次结构图和委托加载原理,解释先前将ClassLoaserTest输出成jre/lib/ext目录下的itcast.jar包
        中后,运行结果为ExtClassLoader的原因。

24. 编写自己的类加载器
        知识讲解:
        自定义类加载器的必须继承ClassLoader
        loadClass方法与findClass方法
        defineClass方法

        程序步骤:
        编写一个对文件内容进行简单加密的程序。
        编写了一个自己的类加载器,可以实现对加密过的类进行装载和解密
        编写一个程序调用类加载器加载类,在源程序中不能用该类名定义引用变量,因为编译器无法识别这个类,
        程序中可以除了使用ClassLoader.load方法之外,还可以使用设置线程的上下文类加载器或者系统类加载器,然后再使用Class.forName。

        实验步骤:
        对不带包名的class文件进行加密,加密结果存放到另外一个目录,例如: java MyClassLoader MyTest.class F:\itcast
        运行加载类的程序,结果能够被正常加载,但打印出来的类装载器名称为AppClassLoader:java MyClassLoader MyTest F:\itcast
        用加密后的类文件替换CLASSPATH环境下的类文件,再执行上一步操作就出问题了,错误说明是AppClassLoader类装载器装载失败。
        删除CLASSPATH环境下的类文件,再执行上一步操作就没问题了。

25. parametered 模板方法设计模式:
        父类中定义好了子类统一的流程,父类中局部细节不能实现的地方,在子类中进行覆盖描述,通过子类实现细节

        父类-->loadClass/findClass(覆盖该方法)/得到Class文件的转换成字节码-->definClass();

26. 有包名的类不能调用没有包名的类    

27. 一个类加载器的高级问题分析
        编写一个能打印出自己的类加载器名称和当前类加载器的父子结构关系链的MyServlet,正常发布后,看到打印结果为WebAppClassloader。
        把MyServlet.class文件打jar包,放到ext目录中,重启tomcat,发现找不到HttpServlet的错误。
        把servlet.jar也放到ext目录中,问题解决了,打印的结果是ExtclassLoader 。
        父级类加载器加载的类无法引用只能被子级类加载器加载的类

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

        编写一个与目标类具有相同接口的代理类,代理类的每个方法调用目标类的相同方法,
        并在调用方法时加上系统功能的代码。 (参看下页的原理图)

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

29. AOP :交叉业务的编程问题即为面向方面的编程(Aspect oriented program ,简称AOP),
        系统中存在交叉业务,一个交叉业务就是要切入到系统中的一个方面,如下所示:
                          安全       事务         日志
        StudentService  ------|----------|------------|-------------
        CourseService   ------|----------|------------|-------------
        MiscService       ------|----------|------------|-------------
        用具体的程序代码描述交叉业务:
        method1         method2          method3
        {                      {                       {
        ------------------------------------------------------切面
        ....            ....              ......
        ------------------------------------------------------切面
        }                       }                       }
        交叉业务的编程问题即为面向方面的编程(Aspect oriented program ,简称AOP),
        AOP的目标就是要使交叉业务模块化。可以采用将切面代码移动到原始方法的周围,
        这与直接在方法中编写切面代码的运行效果是一样的,如下所示:
        ------------------------------------------------------切面
        func1         func2            func3
        {             {                {
        ....            ....              ......
        }             }                }
        ------------------------------------------------------切面
        使用代理技术正好可以解决这种问题,代理是实现AOP功能的核心和关键技术。

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

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

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

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

        代理类的各个方法中通常除了要调用目标的相应方法和对外返回目标返回的结果外,还可以在代理方法中的如下四个位置加上系统功能代码:
                1.在调用目标方法之前
                2.在调用目标方法之后
                3.在调用目标方法前后
                4.在处理目标方法异常的catch块中

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

        BeanFactory的构造方法接收代表配置文件的输入流对象,配置文件格式如下:
                #xxx=java.util.ArrayList
                xxx=cn.itcast.ProxyFactoryBean
                xxx.target=java.util.ArrayList
                xxx.advice=cn.itcast.MyAdvice

        ProxyFacotryBean充当封装生成动态代理的工厂,需要为工厂类提供哪些配置参数信息?
                目标
                通知

        编写客户端应用:
                编写实现Advice接口的类和在配置文件中进行配置
                调用BeanFactory获取对象

32. 传统线程机制的回顾
        a.创建线程的两种传统方式
        在Thread子类覆盖的run方法中编写运行代码
                涉及一个以往知识点:能否在run方法声明上抛出InterruptedException异常,
                以便省略run方法内部对Thread.sleep()语句的try…catch处理?

        在传递给Thread对象的Runnable对象的run方法中编写代码

        总结:查看Thread类的run()方法的源代码,可以看到其实这两种方式都是在调用Thread对象的run方法,
        如果Thread类的run方法没有被覆盖,并且为该Thread对象设置了一个Runnable对象,该run方法会调用Runnable对象的run方法。

        问题:如果在Thread子类覆盖的run方法中编写了运行代码,也为Thread子类对象传递了一个Runnable对象,
        那么,线程运行时的执行代码是子类的run方法的代码?还是Runnable对象的run方法的代码?//子类的
                涉及到的一个以往知识点:匿名内部类对象的构造方法如何调用父类的非默认构造方法。

        多线程机制会提高程序的运行效率吗?为什么会有多线程下载呢?

        b.定时器的应用
                Timer类
                TimerTask类
        
        c.线程的同步互斥与通信
                使用synchronized代码块及其原理
                使用synchronized方法
                分析静态方法所使用的同步监视器对象是什么?
                wait与notify实现线程间的通信
    
33.多个线程访问共享对象和数据的方式
        如果每个线程执行的代码相同,可以使用同一个Runnable对象,这个Runnable对象中有那个共享数据,例如,买票系统就可以这么做。

        如果每个线程执行的代码不同,这时候需要用不同的Runnable对象,有如下两种方式来实现这些Runnable对象之间的数据共享:
            将共享数据封装在另外一个对象中,然后将这个对象逐一传递给各个Runnable对象。
            每个线程对共享数据的操作方法也分配到那个对象身上去完成,这样容易实现针对该数据进行的各个操作的互斥和通信。

            将这些Runnable对象作为某一个类中的内部类,共享数据作为这个外部类中的成员变量,
            每个线程对共享数据的操作方法也分配给外部类,以便实现对共享数据进行的各个操作的互斥和通信,
            作为内部类的各个Runnable对象调用外部类的这些方法。

            上面两种方式的组合:将共享数据封装在另外一个对象中,每个线程对共享数据的操作方法也分配到那个对象身上去完成,
            对象作为这个外部类中的成员变量或方法中的局部变量,每个线程的Runnable对象作为外部类中的成员内部类或局部内部类。

            总之,要同步互斥的几段代码最好是分别放在几个独立的方法中,这些方法再放在同一个类中,这样比较容易实现它们之间的同步互斥和通信。

        极端且简单的方式,即在任意一个类中定义一个static的变量,这将被所有线程共享。

34.线程池
        线程池的概念与Executors类的应用
                创建固定大小的线程池
                创建缓存线程池//线程数可随需求变化
                创建单一线程池
        关闭线程池
                shutdown与shutdownNow的比较
        用线程池启动定时器
                调用ScheduledExecutorService的schedule方法,返回的ScheduleFuture对象可以取消任务。
                支持间隔重复任务的定时方式,不直接支持绝对定时方式,需要转换成相对时间方式

35. Callable&Future
        Future取得的结果类型和Callable返回的结果类型必须一致,这是通过泛型来实现的。
        Callable要采用ExecutorSevice的submit方法提交,返回的future对象可以取消任务。
        CompletionService用于提交一组Callable任务,其take方法返回已完成的一个Callable任务对应的Future对象。

36. Lock&Condition实现线程同步通信
        Lock比传统线程模型中的synchronized方式更加面向对象,与生活中的锁类似,锁本身也应该是一个对象。
        两个线程执行的代码片段要实现同步互斥的效果,它们必须用同一个Lock对象。

        读写锁:分为读锁和写锁,多个读锁不互斥,读锁与写锁互斥,写锁与写锁互斥,这是由jvm自己控制的,你只要上好相应的锁即可。
        如果你的代码只读数据,可以很多人同时读,但不能同时写,那就上读锁;
        如果你的代码修改数据,只能有一个人在写,且不能同时读取,那就上写锁。总之,读的时候上读锁,写的时候上写锁!

        在等待 Condition 时,允许发生“虚假唤醒”,这通常作为对基础平台语义的让步。对于大多数应用程序,这带来的实际影响很小,
        因为 Condition 应该总是在一个循环中被等待,并测试正被等待的状态声明。某个实现可以随意移除可能的虚假唤醒,
        但建议应用程序程序员总是假定这些虚假唤醒可能发生,因此总是在一个循环中等待。

        一个锁内部可以有多个Condition,即有多路等待和通知,可以参看jdk1.5提供的Lock与Condition实现的可阻塞队列的应用案例,
        从中除了要体味算法,还要体味面向对象的封装。在传统的线程机制中一个监视器对象上只能有一路等待和通知,
        要想实现多路等待和通知,必须嵌套使用多个同步监视器对象。(如果只用一个Condition,两个放的都在等,一旦一个放的进去了,
        那么它通知可能会导致另一个放接着往下走。)

---------------------- android培训 、 java培训 、 期待与您交流! ----------------------详细请查看:www.itheima.com

0 0