一、异常
1.异常的结构
1)Error : JVM错误,无法处理
2)Exception:(可处理)
(1)运行期异常
ArrayIndexOutOfBoundException
NullPointerException
(2)编译期异常
InterruptedException
ClassNotFoundException
NoSuchFiledException
NoSuchMethodException
2.异常
异常处理(一般只针对编译期异常)->五个关键字
1)try
try{
//可能发生异常的代码
arr[1]=10;
Thread.sleep(1000);
//return之前调用finally进行退出
return;
}
catch(ArrayIndexOutOfBoundsException e){//关心的异常,不关心的用基类Exception处理
e.PrintStackTrace();//先用子类处理,再用父类处理
}
catch(Exception e){
e.PrintStackTrace();
}finally{
//特点是无论是否会发生异常必定会被执行到
System.out.println("finally");
}
注意:
一个try块中存在多条可能会发生异常的代码A/B/C,如果A发生异常,此时B、C语句是不会被执行到
try块尽量避免嵌套
2)catch:
catch(NullPointerException){
//先用关心的子类处理,再用父类处理
}
尽量避免过细划分异常
3)finally
- final finally finalize 的区别? 如果try块里包含
- return;并且finally块也包含return(从finally块return退出)
- 如果try块里面也含有退出System.exit();finally也包含System.exit();
- 问此时从哪里退出?
从try块中的System.exit();中退出
4)throw
throw new EmptyStackException();
扔异常对象
5)throws
void fun() throws InterruptedException{
/
}
调用处:
try{
fun();
}catch(InterruptedException){
e.PrintStackTrace();
}
二、类加载
1.类加载过程
从磁盘加载到JAVA虚拟机的过程,分为三大阶段
1.1装载阶段
类加载器:(继承关系)
null C/C++终极类加载器
如果一个类他的类加载器是启动加载器,此时getClassLoader();
返回值null,C/C++实现
1.2Boostrap ClassLoader 启动类加载器
它用来加载 Java 的核心类,是用原生代码来实现的,并不继承自 java.lang.ClassLoader(负责加载$JAVA_HOME中jre/lib/rt.jar里所有的class,由C++实现,不是ClassLoader子类)。由于引导类加载器涉及到虚拟机本地实现细节,开发者无法直接获取到启动类加载器的引用,所以不允许直接通过引用进行操作。
1.3 Ext ClassLoader 拓展类加载器
它负责加载JRE的扩展目录,lib/ext或者由java.ext.dirs系统属性指定的目录中的JAR包的类。由Java语言实现,父类加载器为null。
1.4App ClassLoader 应用类加载器
它负责在JVM启动时加载来自Java命令的-classpath选项、java.class.path系统属性,或者CLASSPATH换将变量所指定的JAR包和类路径。
标识文件类型:魔数+次版本号+主版本号
过程:双亲委派模型
注:加载类 ->(parent)委派模型
- 找之前是否加载过类,如果加载过,从父类到子类依次尝试加载,将加载产物
- Class对象返回。如果没有加载过,依次在其父类中查找,直到Boostrap ClassLoader。
- 路径找不到加载失败 ClassNotFoundException
路径找不到加载失败 ClassNotFoundException
双亲委派模型的优点: - 安全性
- 避免类的重复加载
类加载器加载Class大致要经过如下8个步骤:
1.检测此Class是否载入过,即在缓冲区中是否有此Class,如果有直接进入第8步,否则进入第2步。
2.如果没有父类加载器,则要么Parent是根类加载器,要么本身就是根类加载器,则跳到第4步,如果父类加载器存在,则进入第3步。
3.请求使用父类加载器去载入目标类,如果载入成功则跳至第8步,否则接着执行第5步。
4.请求使用根类加载器去载入目标类,如果载入成功则跳至第8步,否则跳至第7步。
5.当前类加载器尝试寻找Class文件,如果找到则执行第6步,如果找不到则执行第7步。
6.从文件中载入Class,成功后跳至第8步。
7.抛出ClassNotFountException异常。
8.返回对应的java.lang.Class对象。
1.2链接阶段
1.2.1验证
- 主次版本号
- 文件格式验证
1.2.2准备
- 变量所使用的内存在方法区中进行分配
- count在方法区分配4字节内存,并赋类型默认值为0.
1.2.3解析
- 将符号引用改为直接引用的过程
3.初始化阶段:
给静态变量赋值操作:count=10;
自定义类加载器:
1.根据路径找到字节码文件 findClass文件
2.采用双亲委派模型加载 loadClass方法
3.加载字节码文件生成对应的Class对象
1. 3.初始化阶段:
自定义类加载器:
1.根据路径找到字节码文件 findClass文件
2.采用双亲委派模型加载 loadClass方法
3.加载字节码文件生成对应的Class对象
初始化一个类的子类(会首先初始化子类的父类)
2.反射
生成对象
第一步:
获取当前类的Class对象:
Class c=People.class;
class people{
//public static Class class;
隐含记录Class对象(加载阶段产生)存储位置
}
Class c=Class.forName("com.tulun.scr7.People);
//异常
People p=new People();
Class c=p.getClass();//getClass()->继承自Object里的方法
三、内部类
内部类包含:实例内部类、静态内部类、匿名内部类、局部内部类
3.1实例内部类
public class TestDemo {
// private int age;
// public class Test{
// private String name;
// public Test(){
// this.name=name;
// TestDemo.this.age=age;
// }
// }
//}
3.2静态内部类
public class TestDemo{
private int age;
public static class Test{
private String name;
public Test(){
this.name=name;
}
}
实例内部类与静态内部类的区别:
-
实例内部类包含两个this(指向当前对象,外部类对象的this)
-
静态内部类包含一个this->指向当前对象的this
3.2匿名内部类
向上造型:
基类(基接口)的引用,引用派生类对象(实现该接口的类对象)
public interface InterfaceA{
InterfaceA interfaceA=new InterfaceA() {
@Override
protected void finalize() throws Throwable {
super.finalize();
}
};
}
3.4局部内部类
局部内部类是定义在一个方法或者一个作用域里面的类,它和成员内部类的区别在于局部内部类的访问仅限于方法内或者该作用域内。
class People{
public People() {
}
}
class Man{
public Man(){
}
public People getWoman(){
class Woman extends People{ //局部内部类
int age =0;
}
return new Woman();
}
}
访问修饰符:
1.修饰成员:
- public 默认 protected private
- protected <=>默认 包访问权限
- 继承:子类和父类定义在不同包下,子类可以访问父类修饰用protected
2.修饰类: - 修饰外部类:public 默认
- 修饰内部类:默认 public private protected
3.5实例内部类对象与静态内部类对象
public class TestDemo{
public static void main(String[] args) {
//实例内部类对象:
// OutClass.InnerClass i =new OutClass().new InnerClass();
//静态内部类对象:
OutClass.InnerClass i =new OutClass.InnerClass();
}
}
四、多态的实现
一个函数名根据对象不同对应不同实现
多态实现原理
动多态的实现原理:地址覆盖
1)静多态(编译期多态/静态绑定)-> 重载(覆盖)
重载条件:在一个类,函数名相同,参数列表不同
2)动多态(运行期多态/动态绑定)->重写
发生动态绑定的条件:
a.继承 b.重写 c.向上造型(基类的引用 引用派生类对象)
Animal a=new Cat();//是一个
Object o=new Integer();
4.1抽象
关键字:abstract
如果一个类包含抽象方法,那么该类必须是抽象类;是抽象类但不一定含有抽象方法
任何子类必须重写父类的抽象方法,或者声明自身为抽象类。
interface FlyInter {
void fly();
}
public abstract class FlyGroups implements FlyInter{
public String name = "飞行类";
public String getName(){
return name;
}
abstract void fun();
}
public class Airplane extends FlyGroups {
@Override
public void fly() {
String name = super.getName();
System.out.println(name);
}
@Override
public void fun(){
}
}
4.2接口
接口与类的区别:
- 接口不能用于实例化对象。
- 接口没有构造方法。
- 接口中所有的方法必须是抽象方法。
- 接口不能包含成员变量,除了 static 和 final 变量。
- 接口不是被类继承了,而是要被类实现。
- 接口支持多继承。
接口特性 - 接口中每一个方法也是隐式抽象的,接口中的方法会被隐式的指定为 public
abstract(只能是 public abstract,其他修饰符都会报错)。 - 接口中可以含有变量,但是接口中的变量会被隐式的指定为 public static final 变量(并且只能是 public,用 private 修饰会报编译错误)。
- 接口中的方法是不能在接口中实现的,只能由实现接口的类来实现接口中的方法。
- 接口是隐式抽象的,当声明一个接口的时候,不必使用abstract关键字。
- 接口中每一个方法也是隐式抽象的,声明时同样不需要abstract关键字。
- 接口中的方法都是公有的。
抽象类和接口的区别
- 抽象类中的方法可以有方法体,就是能实现方法的具体功能,但是接口中的方法不行。
- 抽象类中的成员变量可以是各种类型的,而接口中的成员变量只能是 public static final 类型的。
- 接口中不能含有静态代码块以及静态方法(用 static 修饰的方法),而抽象类是可以有静态代码块和静态方法。
- 一个类只能继承一个抽象类,而一个类却可以实现多个接口。
interface 接口名称 [extends 其他的接口名] {
// 常量
// 抽象方法
}
可继承
interface A{
void funA();
}
interface B extends A{
void funB();
}
4.3方法重写与重载
4.3.1方法重写
若子类中的方法与父类中的某一方法具有相同的方法名、返回类型和参数表,则新方法将覆盖原有的方法。 如需父类中原有的方法,可使用super关键字,该关键字引用了当前类的父类。方法重写是要注意权限的问题,子类中的权限不能小于父类的权限,当父类的权限为private时,子类无法继承。也就无法产生所谓的重写。(修饰符高低:private < 默认修饰符(produced) < public)
public class Parent{
public void fun(){
}
}
public class Child extends Parent{
@Override
public void fun(){
//子类的重写
}
}
4.3.2方法重载
方法的重载是方法的类同,方法名相同,参数列表不同。
public class Demo {
//一个普通得方法,不带参数,无返回值
public void add(){
//method body
}
//重载上面的方法,并且带了一个整形参数,无返回值
public void add(int a){
//method body
}
//重载上面的方法,并且带了两个整型参数,返回值为int型
public int add(int a,int b){
//method body
return 0;
}
}