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

来源:互联网 发布:iuni u3 用电信网络 编辑:程序博客网 时间:2024/05/16 07:53

-------Android培训、Java培训、期待与您交流! ----------

静态导入

静态导入——>import static语句导入一个类中的某个静态方法或所有静态方法,这将使代码更简洁, 提高编码效率。

import语句可以导入一个类或某个包中的所有类

import static语句导入一个类中的某个静态方法或所有静态方法

语法举例: 

import static java.lang.Math.max;

import static java.lang.Math.*; 

可变参数与for循环增强

可变参数解决的问题是:一个方法接受的参数个数不固定

可变参数的特点:

只能出现在参数列表的最后

...位于变量类型和变量名之间,前后有无空格斗可以

调用可变参数的方法时,编译器为该可变参数隐含创建一个数组,在方法体中以数组的形式访问可变参数

增强的for循环

语法:

for(type 变量名:集合变量名){ ... }

注意事项:

迭代变量必须在()中定义

集合变量可以是数组或实现了Iterable接口的集合类

例子:

public static int add(int x, int... args){int sum = x;for(int arg:args){sum += arg;}return sum;}


 

基本数据类型的自动拆箱与装箱

基本数据类型的自动拆箱和装箱——>无需在基本类型(例如double)和包装类型(例如Double)之间人工地进行转换。 方便了基本类型数据和它们包装类地使用 

Integer iObj = 3; //将一个基本数据类型自动装箱

System.out.println (iObj + 12); //自动拆箱 

枚举

Enum是抽象类它可以实现接口、或继承抽象类。

枚举类默认是Enum的子类,所以不可以继承类(java是单继承,它已经继承了Enum),枚举类可以是抽象类(类里定义抽象方法,枚举值就要以匿名内部类的形式去实现它)可以是普通类,但可以实现接口

为什么需要枚举?
一些方法在运行时,它需要的数据不能是任意的,而必须是一定范围内的值,此类问题在JDK5以前采用自定义带有枚举功能的类解决,Java5以后可以直接使用枚举予以解决。

JDK 5新增的 enum 关键字用于定义一个枚举类。

Java中声明的枚举类,均是java.lang.Enum类的子类,它继承了Enum类的所有方法。常用方法:
name()
ordinal()

枚举类具有如下特性:
枚举类也是一种特殊形式的Java类。
枚举类中声明的每一个枚举值代表枚举类的一个实例对象。
与java中的普通类一样,在声明枚举类时,也可以声明属性、方法和构造函数,但枚举类的构造函数必须为私有的
枚举类也可以实现接口、或继承抽象类。
JDK5中扩展了swith语句,它除了可以接收int, byte, char, short外,还可以接收一个枚举类型。
若枚举类只有一个枚举值,则可以当作单态设计模式使用。

下面是一个枚举类的例子:

//枚举类就是一个java类,也可以声明属性,方法,构造函数public enum Grade4 {A("90-100"),B("80-89"),C("70-79"),D("60-69"),E("0-59");private String value;private Grade4(String value) {this.value = value;}public String getValue() {return value;}}//枚举类就是一个java类, 也可以继承抽象和实现接口public enum Grade5 {// 抽象类不能创建实例对象A("90-100"){// new了一个Grade5的子类实例public String toLocaleString() {return "优";}},B("80-89"){// new了一个Grade5的子类实例public String toLocaleString() {return "良";}},C("70-79"){// new了一个Grade5的子类实例public String toLocaleString() {return "中";}},D("60-69"){// new了一个Grade5的子类实例public String toLocaleString() {return "差";}},E("0-59"){// new了一个Grade5的子类实例public String toLocaleString() {return "不及格";}};private String value;private Grade5(String value) {this.value = value;}public String getValue() {return value;}// 对外提供一个方法,返回枚举的本地信息// 一个方法不知道如何实现,可以定义为抽象的public abstract String toLocaleString();}  

 

反射

反射

什么是反射

反射就是在运行状态把Java 类中的各种成分映射成相应相应的Java 类,可以动态得获取所有的属性以及动态调用任意一个方法。

一段java代码在程序的运行期间会经历三个阶段:source-->class-->runtime

Class对象

在java中用一个Class对象来表示一个java类的class阶段

Class对象封装了一个java类定义的成员变量、成员方法、构造方法、包名、类名等。

反射怎么用

获得java类的各个组成部分,首先需要获得代表java类的Class对象

获得Class对象有以下三种方式:

Class.forname(className)            用于做类加载

obj.getClass()                              用于获得对象的类型

类名.class                                    用于获得指定的类型,传参用

反射类的构造方法,获得实例

Class clazz = 类名.class;

Constuctor con = clazz.getConstructor(new Class[]{paramClazz1,paramClazz2,.....});

con.newInstance(params....);

反射类的成员方法

Method m = clazz.getMethod(methodName,new Class[]{paramClazz1,paramClazz2,.....});

m.invoke();

反射类的属性

Field field = clazz.getField(fieldName);

field.setAccessible(true);//设置为可访问

filed.setObject(value);          //设置值

Object value = field.get(clazz);      //获得值

Object staticValue = filed.get(Class);     //获得静态值

JavaBean内省

什么是内省

通过反射的方式操作JavaBean的属性,jdk提供了PropertyDescription类来操作访问JavaBean的属性,Beantils工具基于此来实现。BeanUtils工具包 来操作JavaBean 的

内省怎么用

操作一个属性

Object obj = new Object();

PropertyDescriptor pd = new PropertyDescriptor(propertyName,Class);     //声明属性描述对象,一次只可描述一个属性

Method m = pd.getWriterMethod();//获取setter方法

m.invoke(obj,value);

Method m = pd.getReaderMethod();//获取getter方法

Object value = m.invoke(obj);

操作多个属性

BeanInfo bi = Instospector.getBeanInfo(beanClass);//获取Bean描述对象

PropertyDescriptor[] pds = bi.getPropertyDescriptors();//获取属性描述对象数组

拿到属性描述对象数组之后再循环数组,剩余的操作就跟"操作一个属性"相同了。

两者区别

反射可以操作各种不同的java类,那么内省只是通过反射来操作JavaBean的。

应用

框架就是将开发中大量重复的代码其中起来写个通用的程序,框架是给将来的程序使用的,需要现在的类调用将来的,所以框架要运行是依赖于将来的类实现的。

我们只需要针对接口进行调用,将来的类实现接口,那么方法就固定了。但是将来写的类的类名我们无从得知,这时就需要调用者通过配置文件告诉框架具体的类名。

将java的反射以及内省机制应用到程序设计中可以大大提高程序的智能化和可扩展性。有很多项目都是使用这两种技术实现其核心功能的。例如BeanUtils,Struts等。

泛型

概念介绍:

Java泛型(generics)是JDK 5中引入的一个新特性,允许在定义类和接口的时候使用类型参数(type parameter)。声明的类型参数在使用时用具体的类型来替换。泛型最主要的应用是在JDK 5中的新集合类框架中。

引入泛型的好处

将运行时期出现问题ClassCastException,转移到了编译时期。

方便于程序员解决问题。让运行时问题减少,提高了程序安全性。

避免了强制转换麻烦 

定义泛型: 

定义泛型跟定义原生类型没有什么区别,只是在类或接口后面加入了一个尖括号,尖括号里面是一个类型参数(定义时就是一个格式化的类型参数,在调用时会使用一个具体的类型来替换该类型)

泛型的特点:

泛型的最大特就是类型可擦除。

Java中的泛型基本上都是在编译期来实现的。在生成的Java字节代码中是不包含泛型中的类型信息的。使用泛型的时候加上的类型参数,会被编译器在编译的时候去掉。这个过程就称为类型擦除。

泛型的兼容性设计

关于泛型的转化兼容设计:

代码如下:

//原始类型可以和泛型类型相互赋值  

ArrayList<String> list1 = new ArrayList();  

ArrayList list2 = new ArrayList<String>();  

通配符?

很容易发现,使用泛型的版本也只能接受元素类型为Object类型的集合如ArrayList<Object>();如果是ArrayList<String>,则会编译时出错。那么如何改造新版本以便它能接受所有类型的集合呢?这个问题就可以通过使用通配符来解决。

<>:什么时候用呢?

当操作的引用数据类型不确定的时候,就使用<>。将要操作的引用数据类型传入即可。其实<>就是一个用于接收具体引用数据类型的参数范围。

在程序中,只要用到了带有<>的类或者接口,就要明确传入的具体引用数据类型。

泛型的擦除和补偿:

泛型技术是给编译器使用的技术,用于编译时期,确保了类型的安全。

泛型的擦除:运行时,会将泛型去掉,生成的class文件中是不带泛型的。

为什么擦除呢?为了兼容运行的类加载器。

泛型的补偿:在运行时,通过获取元素的类型进行转换动作,不用使用者再强制转换了。

注意:泛型内只能定义引用数据类型,不能定义基本数据类型。

泛型的通配符和泛型的限定: 

? 未知类型。

泛型的限定:

? extends Person 接收Person类型或者Person的子类对象。上限。一般在存储元素的时候使用

? super Person 接收Person类型或者Person的父类对象。下限。通常对集合中的元素进行去除操作时使用。

类加载器

什么是类加载器

类加载器:加载类的工具.

类加载器的理解

对于System类 首先jvm把这个类的字节码加载到内存里来,通常这个字节码的原始信息放在硬盘上,clsspath指定的目录下,我们.class文件下的内容加载到硬盘里面来再对它进行一些处理,处理完的结果就是字节码就把那个.class文件 从硬盘加载进来在对它进行一些处理这些工作是类加载在做的。

(.class文件 从硬盘加载进来在对它进行一些处理———类加载———》字节码——jvm——》这个类的字节码加载到内存里来)

类加载器的作用:类加载器是JVM的一部分,就是把.class文件加载到硬盘里,然后对它进行一些处理,使其变成字节码文件然后将字节码加载进入JVM执行引擎,以供执行。

Bootstrap Loader加载器,它由C++编写。JVM中另外两个内置类加载器是ExtClassLoader和AppClassLoader,它们是Java类,定义在sun.misc.Launcher.class中,为内部类,且由Bootstrp Loader加载进入虚拟机。

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

类加载器的委托机制

当Java虚拟机要加载一个类时,到底派出哪个类加载器去加载呢?

首先当前线程的类加载器去加载线程中的第一个类。如果类A中引用了类B,Java虚拟机将使用加载类A的类装载器来加载类B。 还可以直接调用ClassLoader.loadClass()方法来指定某个类加载器去加载某个类。

类加载器的委派模型:假设AppClassLoader需要加载一个类,它会首先委托其父加载器ExtClassLoader来加载此 类,ExtClassLoader也会递归性的委托其父加载器Bootstrap Loader来加载此类,如果Bootstrap Loader在sun.boot.class.path下找到被加载类时即加载,如果无法找到时再依次由子类加载器去加载。当所有祖宗类加载器没有加载到类,回到发起者类加载器,还加载不了,则抛ClassNotFoundException,不是再去找发起者类加载器的儿子,因为没有getChild方法。

编写自己的类加载器

要点:

①用户自己定义的类加载器必须继承ClassLoader类
②覆盖findClass()方法(模板设计模式,若是覆盖了loadClass方法,那么用户就必须自己定义向上传递加载请求等一系列功能的语句)
③覆盖defineClass()方法:将传入的class文件对应的二进制传进去变为对应的字节码

编程步骤:

编写一个对文件内容进行简单加密的程序。

编写了一个自己的类装载器,可实现对加密过的类进行装载和解密。

编写一个程序调用类加载器加载类,在源程序中不能用该类名定义引用变量,因为编译器无法识别这个类。程序中可以除了使用ClassLoader.load方法之外,还可以使用设置线程的上下文类加载器或者系统类加载器,然后再使用Class.forName。

动态代理

理解代理

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

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

AOP
OOP(Object oriented program):面向对象编程。
AOP(Aspect oriented program):面向方面编程。

交叉业务的编程问题即为面向方面的编程(Aspect oriented program ,简称AOP),AOP的目标就是要使交叉业务模块化。
代理是实现AOP功能的核心和关键技术。只要是面向方面的编程就涉及到代理。

动态代理技术

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

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

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

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

1.在调用目标方法之前

2.在调用目标方法之后

3.在调用目标方法前后

4.在处理目标方法异常的catch块中

 


 

 

原创粉丝点击