【期末考试季】JAVA进阶复习提纲

前言

作为一块后端没有太多经验的年糕,下周要考试了,所以我必须得来好好复习一下我的JAVA进阶课/(ㄒoㄒ)/~~。这个学期主要是学了:

  • 泛型
  • 反射
  • 线程
  • JDBC
  • JAVA WEB基础
  • Servlet
  • session&cookie
  • 过滤器&监听器

泛型

定义:Java的参数化类型被称为泛型。
出现原因:JAVA不支持多继承,虽然有接口,但还是有约束,必须要实现接口的方法。
注意点:

  1. 虚拟机没有泛型类型对象。比如定义了ArrayList<String>,实际上并没有这个class的存在。
  2. 泛型类型对象之间没有关系,就算T之间互为父子关系,也没有任何关系。
  3. 不能用基本类型实例化类型参数。
  4. 运行时类型查询只适用于原始类型。if( a instanceof Pair<String>) //error
  5. 不能创建参数化的数组。声明类型为Pair<String>[]的变量仍是合法的。不过不能用new Pair<String>[10]初始化这个变量,但可以用(Pair<String>[])new Pair<?>来赋值,可能会找不到类。
  6. 不能实例化类型变量。如new T(), new T[...]T.class都是无效的。
  7. 泛型类的静态上下文中类型变量无效。
  8. 不能捕获或抛出泛型类的实例。
    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<?>的子类。

限定:

  1. 设置上限:? extends Shape,必须是Shape/Shape的子类才可以。
  2. 设置下限:? 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将会连续完成这三个步骤,即类的加载/初始化。

三个步骤:

  1. 加载——找到.class文件并把这个文件包含的字节码加载到内存中
  2. 连接——分为验证、准备和解析
  3. 初始化——类中静态属性和静态块的执行

JVM进程终止的情况:

  1. 运行到最后正常结束
  2. 运行到使用System.exit()/Runtime.getRuntime().exit()
  3. 遇到未捕获的异常或错误
  4. 所在平台强制结束JVM进程。

步骤-加载

调用ClassLoader的findClass方法,可从不同来源中加载类的二进制数据,通常由如下来源:

  1. 本地文件系统
  2. JAR包,例:JDBC编程用到的数据库驱动类
  3. 网络加载,例:Applet
  4. 其他文件生成,例:JSP文件生成对应的Class类
  5. 运行时计算生成,例:动态代理技术

步骤-连接

  1. 验证:检查被加载的类是否有正确的内部结构,并和其他类一致。包括文件格式验证、元数据验证、字节码验证、符合引用验证
  2. 准备:为类的静态属性分配内存和指定初始值(通常情况下为默认初始值)。这些变量所使用的的内存在方法区被分配。
  3. 解析:将常量池中的符号引用替换为直接引用的过程。主要针对类和接口、字段、类方法、接口方法、方法类型、方法句柄和调用点限定符。

注意:

  1. public static int value = 123,变量value在准备阶段的值是0,注意是分配默认值。假设一个变量的定义如下:
  2. public static final int value = 123;变量value在准备阶段的值是123,因为这是一个常量,存放在方法区的常量池中。
  3. 解析过程不一定发生在初始化之前,可以发生在初始化之后再开始。

步骤-初始化

编译器自动收集类中所有类变量的赋值动作和静态语句块中的语句,收集的顺序由语句在源文件中出现的顺序所决定的。

public class Test {
    static int a = 5;  //准备阶段的初值为0,初始化赋值为5
    static int b; //准备阶段的初值为0
    static int c; //准备阶段的初值为0
    static{   
     //初始化阶段的赋值为6
      b = 6; 
     }
}

初始化一个类的步骤

  1. 类没有被加载,先加载并连接该类。
  2. 类的直接父类还被初始化,先初始化其直接父类。
  3. 类中有初始化语句,系统依次执行这些初始化语句。

初始化类的5中情况

  1. 创建类的实例;读取或设置一个类的静态字段(放入常量池的除外);调用一个类的静态方法。
  2. 使用java.lang.reflect包方法进行反射调用(如果没有进行过初始化)。例:Class.forName("SuperClass")
  3. 父类没有进行初始化,则需要先触发父类的初始化
  4. 虚拟机启动,用户需制定一个执行的主类(包含main()方法的那个类),虚拟机会先初始化这个类。
  5. 来自JDK1.7:一个MethodHandle实例最后的解析结果REF_getStatic、REF_putStatic、REF_invokeStatic的方法句柄,且句柄所对应的类没有进行初始化。

注意

  1. 使用ClassLoader类的loadClass()加载某个类时并不会执行该类的初始化。
  2. 如果final类型的静态属性的值不能在编译时得到,必须等到运行时才能确定该属性的值,就会触发初始化。

类加载器

将.class文件加载到内存中,生成对应的java.lang.Class对象。
注意:
只有类是同一个类加载器加载才有可能等于(包含Class对象的equals方法、instanceof)。

类加载器分类

  1. Bootstrap ClassLoader:根类加载器,加载Java的核心类。
  2. Extension ClassLoader:扩展类加载器,加载JRE的扩展目录(JAVA_HOME/jre/lib/ext)中的JAR的类包。
  3. System ClassLoader:系统类加载器,加载命令java中的classpath选择的JAR包和类路径。

类加载机制

  1. 全盘负责:一个类加载器负责加载Class和它的依赖Class,除非显示使用另一个加载器。
  2. 父类委托:先让父类加载该Class,在父类加载器无法加载时从自己的类路径中加载。(类加载器之间的父子关系不是继承上的父子关系,是类加载器实例之间的关系。

  1. 缓存机制:当程序中需要Class时,先从缓存中搜寻,缓存中不存在时,才重读该类对应的二进制数据,并将其转换为Class对象,并存入到cache。

反射

使用场合:编译的时候无法获悉类型,依靠运行时信息发现,这时就采用反射。

获取Class的方法

  1. Class类的forName()静态方法(可能抛出ClassNotFoundException)。
  2. 调用某个类的class属性。
  3. 调用某个对象的getClass()

获取构造函数

  1. Constructor<T> getConstructor(Class<?>..ParameterType)获取Class对象表示类的某个public构造器。
  2. Constructor<?>[] getConstructors()获取Class对象表示类的所有public构造器。
  3. Constructor<T> getDeclaredConstructor(Class<?>..ParameterType)获取Class对象表示类的指定构造器。
  4. Constructor<?>[] getDeclaredConstructors()获取Class对象表示类的所有构造器。

创建对象

  1. Class对象的newInstance()方法:要求该Class对象有默认的构造方法。
  2. 调用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");
  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值