前言
昨日休整一天,今日如梦初醒甚是惭愧,下定决心努力学习,奋勇向前!
一、多态的理解
理解
目前理解为多态主要靠重载和重写实现。
- 重载:在一个类里面,方法名字相同,而参数不同。返回类型可以相同也可以不同。
重载就是多态!!
- 重写:子类对父类的允许访问的方法的实现过程进行重新编写, 返回值和形参都不能改变。即外壳不变,核心重写!
Person s1 = new Student();
当s1调用方法如s1.run()
时进行以下步骤:
- 看父类有没有
run()
方法,如果没有则报错(编译看左,即编译看父类) - 父类有
run()
方法,且子类没有重写run()
方法,此时调用父类的run()
方法 - 父类有
run()
方法,且子类重写run()
方法,此时调用子类的run()
方法(运行看右,即运行看子类是否重写) - 如果父类没有
run()
方法,而子类有run()
方法,则可以通过强制类型转换将s1转换为Student类然后才能执行子类方法。
代码
Fu
public class Fu {
public void fun1(){
System.out.println("Fu fun1");
}
public void fun2(){
System.out.println("Fu fun2");
}
}
Zi
public class Zi extends Fu{
@Override
public void fun1() {
System.out.println("Zi fun1");
}
public void fun3(){
System.out.println("Zi fun3");
}
}
Application
public class Application {
public static void main(String[] args) {
//调用方法run()
Fu obj = new Zi();
obj.fun1();
obj.fun2();
((Zi)obj).fun3();
}
}
运行结果:
Zi fun1
Fu fun2
Zi fun3
(无法直接运行obj.fun3()
必须强制转类型)
另:三种方法不可被重写
- static 方法,属于类,不可被重写
- final 方法,常量
- private 方法,私有
二、instanceof的用法
用法介绍
公式
instanceof (类型转换)引用类型,判断一个对象是什么类型。
A a = new B();
a instanceof X;
A是父类引用,B是子类对象;
比较流程
- 判断A(引用类型,即父类)和X的关系,当X为A的子类时才可进行下一步,否则编译阶段即报错。
- 判断B和X的关系,如果X是B的父亲或本身,则返回true;否则,如果X是B的子类或同级则返回false;
代码
Person
public class Person {
public void run(){
System.out.println("run");
}
}
Student
public class Student extends Person{
public void go(){
System.out.println("son go");
}
}
Applocation1
public class Application1 {
public static void main(String[] args) {
//关系:
//Object>String
//Object>Person>Student
//Object>Person>Teacher
//引用类型为Object时(祖宗类)
Object object = new Student();
System.out.println(object instanceof Student);
System.out.println(object instanceof Person);
System.out.println(object instanceof Object);
System.out.println(object instanceof Teacher);
System.out.println(object instanceof String);
System.out.println("==============");
//引用类型为Person时
Person person = new Student();
System.out.println(person instanceof Student);
System.out.println(person instanceof Person);
System.out.println(person instanceof Object);
System.out.println(person instanceof Teacher);
//System.out.println(person instanceof String);//编译即报错
System.out.println("=============");
//引用类型为Student时(祖宗类)
Student student = new Student();
System.out.println(student instanceof Student);
System.out.println(student instanceof Person);
System.out.println(student instanceof Object);
//System.out.println(student instanceof Teacher);//编译即报错
//System.out.println(student instanceof String);//编译即报错
System.out.println("=============");
//引用类型为Person指向对象为Person型时
Person person2 = new Person();
System.out.println(person2 instanceof Student);
System.out.println(person2 instanceof Person);
System.out.println(person2 instanceof Object);
System.out.println(person2 instanceof Teacher);
}
}
运行结果:
true
true
true
false
false
============
true
true
true
false
============
true
true
true
============
false
true
true
false
三、类型转换
规律
- 父引用指向子对象
- 子->父,向上转型
- 父->子,向下转型:强转
- 方便方法调用,减少重复代码
代码
父类和子类代码同上
Application2
public class Application2 {
public static void main(String[] args) {
//类型转换:基本类型转换 高低64 32 16 8
Person obj = new Student();
//obj.go();不能用go方法
//需要将obj转成Student类型
Student student = (Student) obj;
student.go();
//或者((Student) obj).go;
//子转父可能会丢失子方法(向上转型)
Person person = student;
//student转person是低转高,默认转换即可,但可能会丢失方法
//person.go();
}
}
运行结果
son go
四、static详解
- 非静态的方法可以随意调用静态方法;静态方法只能调用静态方法
代码块
public class Student {
{
System.out.println("匿名代码块");
}
static {
System.out.println("静态代码块");
}
public Student(){
System.out.println("构造方法");
}
public static void main(String[] args) {
Student s1 = new Student();
System.out.println("_________");
Student s2 = new Student();
}
}
运行结果:
静态代码块
匿名代码块
构造方法
_________
匿名代码块
构造方法
注意
- 静态代码块只执行一次
- 匿名代码块在构造方法前可以用来赋初始值
对一些想法的测试
如果不构造本类会发生什么呢?
1. 测试多态
public class Student {
{
System.out.println("匿名代码块");
}
static {
System.out.println("静态代码块");
}
public Student(){
System.out.println("构造方法");
}
public static void main(String[] args) {
Person s3 = Student();
}
}
结果:
静态代码块
匿名代码块
构造方法
2. 测试不构造本类
public class Student {
{
System.out.println("匿名代码块");
}
static {
System.out.println("静态代码块");
}
public Student(){
System.out.println("构造方法");
}
public static void main(String[] args) {
Person s4 = Person();
}
}
结果:
静态代码块
总结:
- 多态使用子类的构造器
- static和类一起编译运行,只要加载此类必然且只运行一次
静态导入别的类的静态方法
import static java.lang.Math.random;
public class Test{
public static void main(String[] args) {
System.out.println(random());
//通过静态导入方法,即无需再打Math.random(),只打random()即可;
}
}
五、抽象类
abstract
- 抽象类中可以有抽象方法和普通方法
- 不能new抽象类,只能依靠子类去实现
- 子类必须重写new方法
- 抽象方法必须在抽象类中
- 抽象类有构造方法(用来给子类提供,所有子类必须调用从Object开始的所有父类的构造函数)
- 抽象类可以写静态方法
- 提高开发效率
六、接口
普通:只有具体实现
抽象类:具体实现和规范(抽象方法)都有
接口(Interface):一组规范,不可以写方法体和静态方法,没有构造器
接口本质是契约
接口的所有定义都是抽象的public abstract
接口的所有常量都是public static final
代码
UserService(interface)
public interface UserService {
//常量 public static final
int AGE = 99;
//接口中所有定义的方法都是抽象的public abstract
void add(String name);
void delete(String name);
void update(String name);
void query(String name);
}
TimeService(interface)
public interface TimeService {
void timer();
}
UserSeriviceImp1
//实现接口的类,必须重写接口中的方法
public class UserServiceImp1 implements UserService,TimeService{
@Override
public void add(String name) {
}
@Override
public void delete(String name) {
}
@Override
public void update(String name) {
}
@Override
public void query(String name) {
}
@Override
public void timer() {
}
}
七、内部类
类的内部再定义一个类
代码
Outer
public class Outer {
private int id=10;
public void out(){
System.out.println("这是外部类的方法");
}
//如果Inner被static修饰则不能getID,因为static直接加载,除非把id也改成static
class Inner{
public void in(){
System.out.println("这是内部类的方法");
}
//获得外部类的私有属性
public void getID(){
System.out.println(id);
}
}
//局部内部类
public void method(){
class Inner{
public void in(){
}
}
}
}
//一个java类中可以有多个class类,但只能有一个public class
class A{
public static void main(String[] args) {
}
}
Application
public class Application {
public static void main(String[] args) {
//new
Outer outer = new Outer();
//通过外部类来实例化内部类
outer.new Inner();
Outer.Inner inner = outer.new Inner();
inner.in();
inner.getID();
}
}
Test
public class Test {
public static void main(String[] args) {
//匿名对象的使用,没有名字初始化类,不用将实例保存到变量中
new Apple().eat();
UserService userService = new UserService() {
@Override
public void hello() {
}
};
}
}
class Apple{
public void eat(){
System.out.println("1");
}
}
interface UserService{
void hello();
}
八、异常
什么是异常
Exception,程序运行出现了异常;
异常:程序运行中出现了不期而至的状况
分类
- 检查性异常:代表性的例子是用户错误或问题引起的异常。程序员无法预见,如打开不存在的文件,就会发生一个异常。
- 运行时异常:可能被程序员避免的异常。运行时异常可以在编译时被忽略
- 错误ERROR:错误不是异常是脱离程序员控制的问题。例如栈溢出,它们在编译时也检查不到。
异常体系结构
- Java把异常当作对象处理,定义java.lang.Throwable作为所有异常的超类
- Java API中已经定义了许多异常类,分为两大类,错误Error和异常Exception。
具体介绍
Error
- Error类对象由Java虚拟机抛出,大多数错误和代码编写者的操作无关。
- Java虚拟机运行错误(Virtual MachineError),当JVM不再有继续执行操作所需的内存资源时,会出现
OutOfMemoryError
。这些异常发生时,Java虚拟机(JVM)一般会选择线程终止。 - 还有发生在虚拟机试图执行应用时,如类定义错误(NoClassDefFoundError),链接错误(LinkageError)。这些错误时不可查的,因为它们在应用程序的控制和处理能力之外,而且绝大多数时程序运行时不允许出现的情况。
Exception
- Exception分支中有一个非常重要的子类RuntimeException(运行时异常)
- ArrayIndexOutOfBoundsException(数组下标越界)
- NullPointerException(空指针异常)
- ArithmeticException(算数异常)
- MissingResourceException(丢失资源)
- ClassNotFoundException(找不到类)等异常,这些异常是不检查异常,程序中可以选择捕获异常,也可以选择不处理。
- 这些异常一般是由程序逻辑错误引起的,程序应该从逻辑角度尽可能避免这类异常的发生。
- Error和Exception的区别:Error通常是灾难性的致命错误,是程序无法控制和处理的,当出现这些异常是,Java虚拟机(JVM)会选择终止线程;Exception通常情况下是可以被程序处理的,并且在程序中应该尽可能地去处理这些异常。
处理机制
异常处理的五个关键字:try、catch、finally、throw、throws
代码
Test
public class Test {
//假设捕获多个异常,从小到大!
try {//try监控区域
if (b==0){
throw new ArithmeticException();//主动抛出异常
}
System.out.println(a/b);
//new Test().a();
}catch(Error e){
System.out.println("Error");
}
catch (ArithmeticException e){//catch(想要捕获的参数类型) 捕获异常
System.out.println("Exception");
}catch (Throwable e){
System.out.println("Throwable");
}finally{
//finally一定会被执行,善后工作
System.out.println("finally");
}
//finally可以没有,但try和catch必须要有
//如果是IO异常,需要关闭,来释放资源
}
第一部分总结:
- try后面可以跟多个catch,catch应该由小到大
- finally代码块是一定会被执行的,一般负责善后工作,如:关闭IO流
Test
public class Test {
public static void main(String[] args) {
try {
new Test().test(1,0);
} catch (ArithmeticException e) {
e.printStackTrace();
}
}
//假设这个方法中,处理不了这个异常,方法上抛出异常
public void test(int a,int b) throws ArithmeticException{
if (b==0){
throw new ArithmeticException();//主动抛出异常
}
}
}
第二部分总结
- throws用在方法上,throw用在try catch内;如果有方法里解决不了的异常,就由throws,抛至上层进行处理
Test2
public class Test2 {
public static void main(String[] args) {
int a=1;
int b=0;
//ctrl+alt+t 快速开启环绕菜单
//QQ占用热键,关闭QQ或用ctrl+alt+win+t可以开启
try {
System.out.println(a/b);
} catch (Exception e) {
e.printStackTrace();//打印错误的栈信息
}finally {
}
}
}
第三部分总结
臭QQ搞得我快捷键搞半天,ctrl+alt+t可开启环绕菜单,热键被占多按个win
自定义异常
用户可以自定义异常,只需继承Exception类。
步骤:
- 创建自定义异常类。
- 在方法中通过throw关键字抛出异常对象
- 如果在当前抛出异常的方法中处理异常,可以使用try-catch语句捕获并进行处理;否则在方法的声明中通过throws关键字知名要抛出给方法调用者的异常,继续进行下一步操作。
- 在出现异常方法的调用者中捕获并处理异常
代码
MyException(自定义的异常)
//自定义的异常类
public class MyException extends Exception{
//传递数字>10
private int detail1;
public MyException(int a) {
this.detail1 = a;
}
//toString异常的打印信息
@Override
public String toString() {
return "MyException{" +
"detail1=" + detail1 +
'}';
}
}
- toString就是将实例化对象转换成能输出的字符串类型,自动转换可以sout直接输出
TestMyException测试类
public class TestMyException {
//可能存在异常的方法
static void test(int a) throws MyException{
System.out.println("传递的参数为"+a);
if(a>10){
throw new MyException(a);//抛出
}
System.out.println("ok");
}
public static void main(String[] args) {
try {
test(11);
} catch (MyException e) {
System.out.println("MyException=>"+e);
}
}
}
完成对异常的测试,test成功捕获后抛出给主函数,主函数catch捕获到MyException后执行操作,将捕获到的异常实例e自动通过toString方法,通过sout输出。
异常经验总结
- 处理运行异常时,采用逻辑去合理规避同时辅助try-catch处理
- 在多重catch块后,可以加一个catch(Exception)来处理可能被遗漏的异常
- 对于不确定的代码,也可以加上try-catch,处理潜在的异常
- 尽量处理异常,切忌只是调用printStackTrace()来打印输出
- 具体如何处理,根据不同业务和异常类型去决定
- 尽量添加finally语句去释放占用资源