多态
多态:父类的引用指向子类对象。
注意 :
- 子父类中含有同名的非静态方法时,访问的是子类的方法
- 子父类中含有同名的静态方法时,访问的是父类的方法
- 子父类中含有同名的成员变量时,访问的是父类的成员变量
总结:多态情况下,子父类存在同名的成员时,除去非静态函数时才是访问子类其他访问的都是父类的成员,编译看左边,运行不一定看右边,也就是说静态函数与成员变量编译与运行看左边,非静态函数编译看左边,运行看右边。
多态的应用:
- 多态用于形参类型的时候,可以接收更多类型的数据。
- 多态用于返回值类型的时候,可以返回更多类型的数据。
内部类
内部类的好处:内部类可以直接访问外部类的所有成员。
成员内部类
访问方式:
1.当内部类未被私有化我们可以通过:外部类.内部类 变量名 = new 外部类().new 内部类() ,来建立内部类对应,但是如果内部类被私有化的化只能使用第二种方式
2.外部类提供内部类的实列话的函数。
外部类与内部类访问同名成员变量
当内部类与外部类存在同名变量的情况下,默认是访问内部类的成员变量,如果要访问外部类的成员变量,可以通过:外部类.this.变量/方法的形式进行访问。
class Outer {
public String name = "小明";
class Inner {
public String name = "小张";
public void run() {
System.out.println(Outer.this.name+"跑起来");
}
}
public Inner instance() {
return new Inner();
}
}
class Demo{
public static void main(String[] args) {
//直接建立
Outer.Inner inner = new Outer().new Inner();
inner.run();//小明跑起来
//外部类提供方法构建内部类对应
Outer outer = new Outer();
outer.instance().run();//小明跑起来
}
}
局部内部类
注意:
- 局部内部类就像是方法里面的一个局部变量一样,是不能有 public、protected、private 以及 static 修饰符的。
- JDK8之前,匿名内部类访问的局部变量必须要用final修饰,JDK8开发着自己不用加final,底层是自动帮忙加了final,不过值得注意的是该局部变量还是不能更改值不然会报错。
JDK8之前,匿名内部类访问的局部变量必须要用final修饰的原因?
首先看下生命周期,内部类对象的生命周期和其他对象的生命周期一样:当没有引用指向内存地址时,通过垃圾回收机制回收,下面代码中run函数执行完成后x会立马从内存中消失,而Inner对象在方法执行完毕的时候会等待gc来回收并不是立刻从内存中消失,也就是说可能x对象已经消失了,匿名内部类对象照样可能使用了x,那么为了解决这个问题编译器会把所有局部内部类中访问到的局部变量都拷贝一份,这样的话即使局部变量从内存中消失了也不影响局部内部类的使用,那既然拷贝了如果中途x的值变更了怎么办呢?难道要实时更新吗。所以加了个final,被final修饰的基本类型变量的话是不可以被修改值的,被final修饰的引用类型变量的话是不可以重新指向新的内存地址的。
class Outer {
String name = "外部类";
public void run(){
int x = 10;
//x = 110; 此处更改值会报错
class Inner{
String name = "内部类";
public void print(){
System.out.println(Outer.this.name+x+"打印函数。。");
}
}
new Inner().print();
}
}
class Demo{
public static void main(String[] args) {
new Outer().run();
}
}
静态内部类
特殊的成员内部类,当成员内部类含有静态成员变量时,该内部类必须有static修饰。
class Outer {
static String name = "外部部类";
static class Inner{
static String name = "内部部类";
public void print(){
System.out.println(Outer.name+"打印函数。。");
}
}
}
class Demo{
public static void main(String[] args) {
new Outer.Inner().print();
}
}
匿名内部类
存在条件:必须有继承或者实现
abstract class Person{
public abstract Person run();
public abstract Person eat();
}
class Demo{
public static void main(String[] args) {
Person p = new Person(){
public Person run() {
System.out.println("跑起来");
return this;
}
public Person eat() {
System.out.println("吃起来");
return this;
}
public Person say() {
System.out.println("说起来");
return this;
}
}.say().eat().run();//这种方式可访问所有的函数(包括特有的函数)
p.eat();
p.run();
//p.say();//这种方式不可以访问子类特有的函数
}
}
异常
异常体系:
----------| Throwable 所以异常或者错误类的超类
--------------|Error 错误 错误一般是用于jvm或者是硬件引发的问题,所以我们一般不会通过代码去处理错误的。
--------------|Exception 异常 是需要通过代码去处理的。
------------------| 运行时异常: 如果一个方法内部抛出了一个运行时异常,那么方法上 可以声明也可以不 声明,调用者可以以处理也可以不处理。
------------------| 编译时异常(非运行时异常、受检异常): 如果一个方法内部抛出了一个编译时异常对象,那么方法上就必须要声明,而且调用者也必须要处理。
运行时异常: RuntimeException以及RuntimeException子类 都是属于运行时异常。
编译时异常: 除了运行时异常就是编译异常。
Throwable常用的方法:
toString() 返回当前异常对象的完整类名+病态信息。
getMessage() 返回的是创建Throwable传入的字符串信息。
printStackTrace() 打印异常的栈信息。
异常的处理
捕获处理
格式
try{
可能发生异常的代码;
}catch(捕获的异常类型 变量名){
处理异常的代码....
}
捕获处理要注意的细节:
- 如果try块中代码出了异常经过了处理之后,try-catch块内异常之后的代码不再执行,但是try-catch块外面的代码正常执行。
- 一个try块后面是可以跟有多个catch块的,,但是捕获的异常类型必须从小到大进行捕获,否则编译报错
public class Test {
public static void main(String[] args) throws Exception {
try {
System.out.println("开始");
int a = 100/0;
System.out.println("发送异常了");
}catch (Exception e){
System.out.println("异常处理了");
}
System.out.println("异常处理之后");
//结果:开始 异常处理了 异常处理之后
}
}
抛出处理(throw throws)
抛出处理要注意的细节:
- 如果一个方法的内部抛出了一个异常 对象,那么必须要在方法上声明抛出且调用者必须要处理异常
- 一个方法遇到了throw关键字,该方法会马上停止执行。
throw与throws的区别
throw用于函数体内部抛出一个异常对象,throws用于函数定义时抛出多个异常类型