前言
作为一块后端没有太多经验的年糕,下周要考试了,所以我必须得来好好复习一下我的JAVA进阶课/(ㄒoㄒ)/~~。这个学期主要是学了:
- 泛型
- 反射
- 线程
- JDBC
- JAVA WEB基础
- Servlet
- session&cookie
- 过滤器&监听器
泛型
定义:Java的参数化类型被称为泛型。
出现原因:JAVA不支持多继承,虽然有接口,但还是有约束,必须要实现接口的方法。
注意点:
- 虚拟机没有泛型类型对象。比如定义了ArrayList<String>,实际上并没有这个class的存在。
- 泛型类型对象之间没有关系,就算T之间互为父子关系,也没有任何关系。
- 不能用基本类型实例化类型参数。
- 运行时类型查询只适用于原始类型。
if( a instanceof Pair<String>) //error
- 不能创建参数化的数组。声明类型为
Pair<String>[]
的变量仍是合法的。不过不能用new Pair<String>[10]
初始化这个变量,但可以用(Pair<String>[])new Pair<?>
来赋值,可能会找不到类。 - 不能实例化类型变量。如
new T()
,new T[...]
或T.class
都是无效的。 - 泛型类的静态上下文中类型变量无效。
- 不能捕获或抛出泛型类的实例。
List<String> l1=new ArrayList<String>();
List<Integer> l2=new ArrayList<Integer>();
System.out.println(l1.getClass()==l2.getClass()); //true
Collection c= new ArrayList<String>();
if(c instanceof ArrayList<List>){} //报错
定义方式
泛型类
public class 类名<T>
使用举例:
Apple<String> a1 = new Apple<String>("苹果");
Apple<Double> a2 = new Apple<Double>(5.67);
注意:不能单独用来修饰静态变量和静态方法(方法定义具体看后面)。
泛型接口派生类、子类:
一定要指明T的类型,或者不写<T>(编译器会警告,默认为是Object)
public class A1 extends Apple<String>{}
public class A2 extends Apple{} //等同于<Object>
泛型方法
public <T> void ArrayToCollection(T[] a, Collection<T> c){
//...
}
方法中的泛型参数无须显式传入实际类型参数。编译器根据实参推断类型形参的值。
为了让编译器能够准确的推断出泛型方法中的形参类型,不能产生多种可能性。
比如:我写了一个选出三个变量中中间的那个值的函数。我可以传入字符串比较,也可以传数字,但数字同时有Comparable和Number两个接口,这样它无法确定T应该是哪个,应该写成public static <T extends Comparable<T>> Pair<T> minmax(T[] a)
。
限定多个用&连接,比如T extends Comparable&Serializable
。
类型通配符
泛型必须传入具体的类型,但如果不确定,就可以用类型通配符,用?
表示。?
代表可以使任意类型
如:
public void test(List<?> c){
for (int i = 0; i < c.size(); i++) {
System.out.println(c.get(i));
}
}
关系:
List<?>是List的子类,且List<Integer>、List<String>...都是List<?>的子类。
限定:
- 设置上限:
? extends Shape
,必须是Shape/Shape的子类才可以。 - 设置下限:
? super Apple
,必须是Apple/Apple的父类才可以。
易错:
1.List<?>集合是只读的。不能往List<?>中添加除null
的任何东西。
[原因]我们假设可以添加的话:
List<String> is = Arrays.asList("one", "two", "three");
List<?> list=is;
list.add(new String("four"));//Ok
list.add(new Integer(4));//如果假设成立,则是OK的
那么混入了其他类型的变量我们也没有办法判断,所以要禁止添加。
2.?
不是类型变量,不可以代替类型来使用。
public static void swap(Pair<?> p){
? t=p.getFirst(); //错误
}
类的加载
定义:当程序主动使用某个类时,如果该类还未被加载到内存中,系统会通过加载、连接、初始化三个步骤来该类进行初始化,如果没有意外,JVM将会连续完成这三个步骤,即类的加载/初始化。
三个步骤:
- 加载——找到.class文件并把这个文件包含的字节码加载到内存中
- 连接——分为验证、准备和解析
- 初始化——类中静态属性和静态块的执行
JVM进程终止的情况:
- 运行到最后正常结束
- 运行到使用System.exit()/Runtime.getRuntime().exit()
- 遇到未捕获的异常或错误
- 所在平台强制结束JVM进程。
步骤-加载
调用ClassLoader的findClass方法,可从不同来源中加载类的二进制数据,通常由如下来源:
- 本地文件系统
- JAR包,例:JDBC编程用到的数据库驱动类
- 网络加载,例:Applet
- 其他文件生成,例:JSP文件生成对应的Class类
- 运行时计算生成,例:动态代理技术
步骤-连接
- 验证:检查被加载的类是否有正确的内部结构,并和其他类一致。包括文件格式验证、元数据验证、字节码验证、符合引用验证
- 准备:为类的静态属性分配内存和指定初始值(通常情况下为默认初始值)。这些变量所使用的的内存在方法区被分配。
- 解析:将常量池中的符号引用替换为直接引用的过程。主要针对类和接口、字段、类方法、接口方法、方法类型、方法句柄和调用点限定符。
注意:
-
public static int value = 123
,变量value在准备阶段的值是0,注意是分配默认值。假设一个变量的定义如下: -
public static final int value = 123;
变量value在准备阶段的值是123,因为这是一个常量,存放在方法区的常量池中。 - 解析过程不一定发生在初始化之前,可以发生在初始化之后再开始。
步骤-初始化
编译器自动收集类中所有类变量的赋值动作和静态语句块中的语句,收集的顺序由语句在源文件中出现的顺序所决定的。
public class Test {
static int a = 5; //准备阶段的初值为0,初始化赋值为5
static int b; //准备阶段的初值为0
static int c; //准备阶段的初值为0
static{
//初始化阶段的赋值为6
b = 6;
}
}
初始化一个类的步骤
- 类没有被加载,先加载并连接该类。
- 类的直接父类还被初始化,先初始化其直接父类。
- 类中有初始化语句,系统依次执行这些初始化语句。
初始化类的5中情况
- 创建类的实例;读取或设置一个类的静态字段(放入常量池的除外);调用一个类的静态方法。
- 使用java.lang.reflect包方法进行反射调用(如果没有进行过初始化)。例:
Class.forName("SuperClass")
- 父类没有进行初始化,则需要先触发父类的初始化
- 虚拟机启动,用户需制定一个执行的主类(包含main()方法的那个类),虚拟机会先初始化这个类。
- 来自JDK1.7:一个MethodHandle实例最后的解析结果REF_getStatic、REF_putStatic、REF_invokeStatic的方法句柄,且句柄所对应的类没有进行初始化。
注意
- 使用ClassLoader类的loadClass()加载某个类时并不会执行该类的初始化。
- 如果final类型的静态属性的值不能在编译时得到,必须等到运行时才能确定该属性的值,就会触发初始化。
类加载器
将.class文件加载到内存中,生成对应的java.lang.Class对象。
注意:
只有类是同一个类加载器加载才有可能等于(包含Class对象的equals方法、instanceof)。
类加载器分类
- Bootstrap ClassLoader:根类加载器,加载Java的核心类。
- Extension ClassLoader:扩展类加载器,加载JRE的扩展目录(JAVA_HOME/jre/lib/ext)中的JAR的类包。
- System ClassLoader:系统类加载器,加载命令java中的classpath选择的JAR包和类路径。
类加载机制
- 全盘负责:一个类加载器负责加载Class和它的依赖Class,除非显示使用另一个加载器。
- 父类委托:先让父类加载该Class,在父类加载器无法加载时从自己的类路径中加载。(类加载器之间的父子关系不是继承上的父子关系,是类加载器实例之间的关系。
)
- 缓存机制:当程序中需要Class时,先从缓存中搜寻,缓存中不存在时,才重读该类对应的二进制数据,并将其转换为Class对象,并存入到cache。
反射
使用场合:编译的时候无法获悉类型,依靠运行时信息发现,这时就采用反射。
获取Class的方法
- Class类的
forName()
静态方法(可能抛出ClassNotFoundException
)。 - 调用某个类的
class
属性。 - 调用某个对象的
getClass()
。
获取构造函数
-
Constructor<T> getConstructor(Class<?>..ParameterType)
获取Class对象表示类的某个public构造器。 -
Constructor<?>[] getConstructors()
获取Class对象表示类的所有public构造器。 -
Constructor<T> getDeclaredConstructor(Class<?>..ParameterType)
获取Class对象表示类的指定构造器。 -
Constructor<?>[] getDeclaredConstructors()
获取Class对象表示类的所有构造器。
创建对象
- Class对象的newInstance()方法:要求该Class对象有默认的构造方法。
- 调用Constructor对象的newInstance()。
调用方法
Class对象的getMethods()方法/getMethod()方法,再调用Method Object invoke(Object obj, Object...args)
,该方法中的obj是执行该方法的主调,后面跟着的是参数。
访问属性
获得Class对象后,通过该Class对象的getFields()
方法或getDeclaredFields()
方法来获取全部属性或指定属性。
Field nameField = personClazz.getDeclaredField("name");
nameField.setAccessible(true);
nameField.set(p , "Yeeku.H.Lee");
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。