08.封装继承多态
封装
-
该露的露,该藏的藏
- 我们程序设计要追求 “高内聚,低耦合”。高内聚就是类的内部数据操作细节自己完成,不允许外部干涉;低耦合:仅宝路少量的方法给外部使用。
-
封装(数据的隐藏)
- 通常,应禁止直接访问一个对象中数据的实际表示,而应通过操作接口来访问,这称为信息隐藏
-
记住这句话:属性私有,get/set
-
package OOP.Test10; /* 封装: 1.提高程序的安全性,保护数据 2.隐藏代码的实现细节 3.统一接口 4.系统的可维护性增加了 */ public class Application { public static void main(String[] args) { Student lxk = new Student(); lxk.setName("李小康"); System.out.println(lxk.getName()); lxk.setAge(999); System.out.println(lxk.getAge()); } } -
package OOP.Test10; public class Student { // private 私有 private String name;// 学生姓名 private int age; // 学生年龄 private char sex; // 学生性别 private int id; // 学生号 // 提供一些操作这些属性的方法 // 即提供一些public的get,set的方法 // get()即获得这个属性,set()设定属性的值 public String getName() { return name; } public void setName(String name) { this.name = name; } // Alt+ Insert 自动生成 public int getAge() { if (age>130||age<0){ age = 3; }else { this.age = age; } return age; } public void setAge(int age) { this.age = age; } public char getSex() { return sex; } public void setSex(char sex) { this.sex = sex; } public int getId() { return id; } public void setId(int id) { this.id = id; } void study(){ } }
继承
-
继承的本质是对某一批类的抽象,从而实现对现实世界更好的建模。
-
extends的意思是“扩展”。子类是父类的扩展。
-
JAVA中类只有单继承,没有多继承!一个字类只能有一个父类,而一个父类可以有多个子类
-
继承是类与类之间的一种关系。除此之外,类和类之间的关系还有依赖,组合,聚合等。
-
继承关系的两个类,一个为子类(派生类),一个为父类(基类)。子类继承父类,使用extends关键字来表示。
-
子类和父类之间,从意义上讲应该具有“is a”的关系。
-
object(所有类的父类)
-
package OOP.Test11; public class Application { public static void main(String[] args) { Student student = new Student(); student.say(); // 输出结果:说了一句话 System.out.println(student.money); // 输出结果:1000000000 } } -
package OOP.Test11; import java.util.Scanner; // 父类 public class Persion /*extends Object*/ { public int money = 10_0000_0000; public void say(){ System.out.println("说了一句话"); } public int getMoney(){ return money; } public void setMoney(int money){ this.money = money; } } -
package OOP.Test11; // 子类(派生类) 学生 is 人 // 子类继承了父类,就会拥有父类的全部方法! public class Student extends Persion { // 快捷键 Ctrl+H : 打开继承树 } -
super
-
注意点: 1.super调用父类的构造方法,必须在构造方法的第一个 2.super必须只能出现在子类的方法或者构造方法中! 3.super和this不能同时调用构造方法! 与this的不同点: 代表对象不同: this:本身调用者这个对象 super:代表父类对象的引用 构造方法 this(); // 本类的构造 super(); // 父类的构造
-
-
package OOP.Test12; public class Application { public static void main(String[] args) { Student student = new Student(); // student.test("李小康"); // student.test1(); // 先执行父类的无参构造后执行子类的无参构造 } } -
package OOP.Test12; public class Persion { protected String name = "lixiaokang"; /* // 私有的东西无法被继承 prative void print(){ System.out.println("Persion"); } */ public Persion() { System.out.println("Persion类的无参构造执行了!"); } public void print(){ System.out.println("Persion"); } } -
package OOP.Test12; public class Student extends Persion{ private String name = "lxk"; public void test(String name){ System.out.println(name); // 输出结果:李小康 System.out.println(this.name); // lxk System.out.println(super.name); // 输出结果: lixiaokang } public Student() { // 隐藏代码,调用了父类的无参构造 // super();// 调用父类的构造器,必须位于子类构造器的第一行 System.out.println("Student类的无参构造执行了!"); } public void print(){ System.out.println("Student"); } public void test1(){ print(); // 输出结果:Student this.print(); // 输出结果: Student super.print(); // 输出结果: Persion } } -
方法重写
-
package OOP.Test13; public class Application { // 静态方法和非静态方法的区别很大 public static void main(String[] args) { // 静态方法: // 方法的调用只和左边调用的数据类型有关 // 非静态方法时且非私有方法时:重写 A a = new A(); // a.test(); // 输出结果为:A-->test a.test(); //输出结果为:A-->test // 父类的引用指向子类() B b = new A(); // b.test(); // 输出结果为:B-->test() b.test(); // 输出结果为:A-->test // 子类重写了父类的方法 } } -
package OOP.Test13; public class A extends B{ // public static void test(){ // System.out.println("A-->test()"); // } public void test(){ System.out.println("A-->test()"); } } -
package OOP.Test13; // 重写,都是方法的重写,与属性无关。 public class B { // public static void test(){ // System.out.println("B-->test()"); // } public void test(){ System.out.println("B-->test()"); } } -
重写总结: 重写:需要有继承关系,子类重写父类的方法 1.方法名必须相同 2.参数列表必须相同 3.修饰符:范围可以扩大:public>protected>Default>private 4.抛出的异常:范围:可以被缩小,但不能扩大: 重写,子类的方法和父类必须一致:方法体不同。 什么情况下不能重写: 1.static 方法,属于类,它不属于实例 2.final 常量; 3.private方法 为什么需要重写: 1.父类的功能,子类不一定需要,或者不一定需要满足 快捷键:Alt+Insert
-
多态
-
即同一方法可以根据发送对象的不同而采用多种不同的行为方式。
-
一个对象的实际类型是确定的,但可以指向对象的引用的类型有很多
-
多态存在的条件
- 有继承关系
- 子类重写父类方法
- 父类引用指向子类的对象
-
注意:多态是方法的多态,属性没有多态性
-
package OOP.Test14; public class Application { public static void main(String[] args) { //一个对象的实际类型是确定的 //new Student(); //new Persion(); //可以指向对象的引用类型就不确定了,父类的引用指向子类的类型 // Student 能调用的方法都是自己的或者重父类调用的 Student s1 = new Student(); // Persion 父类型,可以指向子类,但不能调用子类独有的方法 Persion s2 = new Student(); Object s3 = new Student(); // 对象能执行哪些方法,主要看对象左边的类型,和右边的关系不大 s2.run(); // 子类重写了父类的方法,执行子类重写后的方法 s1.run(); s1.eat(); // s2.eat(); // 父类对象不能调用子类独有的方法 } } -
package OOP.Test14; public class Persion { public void run(){ System.out.println("run!!"); } } -
package OOP.Test14; public class Student extends Persion { @Override public void run() { System.out.println("son!!"); } public void eat(){ System.out.println("eat!!"); } } -
多态注意项: 1.多态是方法的多态,属性没有多态 2.父类和子类,有联系 注意类型转换异常(ClassCastException!) 3.存在条件:继承关系,方法的重写,父类的引用指向子类对象! -
instanceof(类型转换) 引用类型,判断一个对象是什么类型
-
package OOP.Test15; public class Application { public static void main(String[] args) { /* Object>String Object>Persion>Teacher Object>Persion>Student */ Object object = new Student(); // X x = new Y() ; // System.out.println(x instanceof Yn); // 当x和Y没有关系时不能编译通过,当Y和Yn没有关系时结果为false,其他情况下为true System.out.println(object instanceof Object); // true System.out.println(object instanceof Persion); // trye System.out.println(object instanceof Student); // true System.out.println(object instanceof Teacher); // false System.out.println(object instanceof String); // false System.out.println("================================"); Persion persion = new Student(); System.out.println(object instanceof Object); // true System.out.println(persion instanceof Persion); // trye System.out.println(persion instanceof Student); // true System.out.println(persion instanceof Teacher); // false // System.out.println(persion instanceof String); // 编译错误,二者不能转换 System.out.println("================================="); Student student = new Student(); System.out.println(student instanceof Object); // true System.out.println(student instanceof Persion); // trye System.out.println(student instanceof Student); // true // System.out.println(student instanceof Teacher); // 编译错误,二者不能转换 // System.out.println(student instanceof String); // 编译错误,二者不能转换 } } -
package OOP.Test15; public class Persion { public void run(){ System.out.println("run"); } } -
package OOP.Test15; public class Student extends Persion { public void go(){ System.out.println("go"); } } -
package OOP.Test15; public class Teacher extends Persion { }
-
-
-
package OOP.Test15; /* 1.父类的引用指向子类的对象 2.把子类转换为父类,自动转换,可能会导致自己原来的方法丢失 3.把父类转换为子类需强制转换 4.方便方法的调用,减少重复的代码 抽象:继承 封装 多态 抽象类 接口 */ public class Application { public static void main(String[] args) { // 类 型 之 间 的 转 换 : 父 子 // 高 低 Persion student = new Student(); // student.go();// 编译报错,Persion里面没有go()方法 // 将student对象的Persion类型转换为Student类型,就可以使用Student类型的方法了 Student student1 = (Student) student; student1.go(); // ((Student) student).go();// 此代码也可代替上面两行代码 // 高 低 Persion persion = student; // 低类型到高类型自动转换 // persion.go(); 编译错误,低类型到高类型会存在低类型中的方法丢失 } } -
package OOP.Test15; public class Persion { public void run(){ System.out.println("run"); } } -
package OOP.Test15; public class Student extends Persion { public void go(){ System.out.println("go"); } } -
package OOP.Test15; public class Teacher extends Persion { } -
static关键字详解
package OOP.Test16; public class Student { private static int age; //静态的变量 private double score; // 非静态成员变量 public void run(){ } public static void go(){ } public static void main(String[] args) { // 静态方法,可以通过类名直接调用。 Student.go();// go() 也可以。 // 非静态方法,必须类创建的对象来调用 new Student().run(); Student s1 = new Student(); System.out.println(Student.age); //score 为非静态成员变量,不能通过类名直接调用。 // System.out.println(Student.score);// 编译错误 System.out.println(s1.age); System.out.println(s1.score); } }-
package OOP.Test16; public class Persion { // 2nd { System.out.println("匿名代码块"); } // 1st static { System.out.println("静态代码块"); } // 3rd public Persion() { System.out.println("构造方法"); } public static void main(String[] args) { /* 输出结果: 静态代码块 匿名代码块 构造方法 */ Persion persion1 = new Persion(); System.out.println("=============="); /* 输出: 匿名代码块 构造方法 分析:静态方法只输出一次; */ Persion persion2 = new Persion(); } }
-
-
抽象类
-
abstract修饰符可以用来修饰方法也可以修饰类,如果修饰方法,那么该方法就是抽象方法;如果修饰类,那么该类就是抽象类
-
抽象类中可以没有抽象方法,但是有抽象方法的类一定要声明抽象类。
-
抽象类,不能使用new关键字来创建对象,它是用来让子类继承的。
-
抽象方法:只有方法的声明,没有方法的实现,它是用来让子类实现的。
-
子类继承抽象类,那么就必须要实现抽象类没有实现的抽象方法,否则该子类也要声明为抽象类。
-
eg:
-
package OOP.Test17; public class Application { public static void main(String[] args) { // Action action = new Action(); //报错,抽象类不能声明对象 } } -
package OOP.Test17; // abstract 抽象类 public abstract class Action { // 约束~ 别人帮我们完成实现 // abstract 抽象方法,只有方法的名字没有方法的实现! public abstract void doSomething(); // 抽象类特点: // 1.不能new这个抽象类,只能靠子类去实现它:约束! // 2.抽象类中可以写普通方法~ // 3.抽象方法必须在抽象类中 // // 思考: // 1.抽象类中存在构造器吗? // 2.存在的意义 } -
package OOP.Test17; // 子类必须重写父类中的抽象方法,除非子类也是抽象类 public class A extends Action { public void doSomething(){ System.out.println("重写父类的抽象方法"); } }
-
-
接口(interfac)
-
普通类:只有具体实现
-
抽象类:具体实现和规范(抽象方法)都有!
-
接口:只有规范!(自己无法写方法~专业的约束!约束和实现分离:面向接口编程)
-
1
-
接口就是规范,定义的是一组规则,体现了现实世界中“如果你是…则必须能…”的思想。如果你是天使,则必须能飞。。。
-
接口的本质是契约,就像我们人间的法律一样,制定好后大家都遵守。
-
接口的精髓,是对对象的抽象,最能体现这一点的就是接口,为什么我们讨论设计 模式都只针对具备了抽象能力的语言(比如c++、java、c#等),就是因为设计模式所研究的,实际上就是如何合理的去抽象。
-
package OOP.Test18; // interface 定义的关键字,接口都需要有实现类 public interface UserService { /* 接口中定义常量 // 常量~ publicstatic final // int AGE = 99; */ // 接口抽象方法中不能有方法体 // public void run(){ //编译错误 // } // 接口中的所有定义其实都是抽象的 public abstract void add(String name); void delete(String name); void updata(String name); void query(String name); }package OOP.Test18; public interface TimeService { void timer(); }package OOP.Test18; /* 抽象类:继承用extends 类 可以实现接口 implement 接口 实现了接口的类,就需要重写接口中的方法~ */ // 利 用 接 口 实 现 了 多 个 继 承 public class UserServiceImp1 implements UserService,TimeService { @Override public void timer() { } @Override public void add(String name) { } @Override public void delete(String name) { } @Override public void updata(String name) { } @Override public void query(String name) { } } -
接口作用
作用: 1.约束 2.定义一些方法,让不同的人实现~ 10---->1 3.public abstract 4.public static final 5.接口不能被实例化~,接口中没有构造方法~ 6.implements 可以实现多个接口 7.必须要重写接口中的方法~
-
-
内部类
-
内部类就是在一个类的内部再定义一个类,比如,A类中定义一个B类,那么B类相对于A类来说就成为内部类,而A类相对于B类来说就是外部类了。
-
成员内部类
-
静态内部类
-
局部内部类
-
匿名内部类
-
package OOP.Test19; public class Application { public static void main(String[] args) { Outer outer = new Outer(); // 通过这个外部类来实例化内部类 Outer.Inner inner = outer.new Inner(); inner.in();// 输出结果为:这是内部类的方法 inner.getId(); // 输出结果为:10 } }package OOP.Test19; public class Outer { private int id = 10; public void out(){ System.out.println("这是外部类的方法"); } public class Inner{ public void in(){ System.out.println("这是内部类的方法"); } // 获得外部类的私有属性~ public void getId(){ System.out.println(id); } } // 一个java类中可以有多个class类,但只能有一个public修饰的类 class A{ } // 局 部 内 部 类 public void method(){ class Inner{ } } }
-
-
-
异常机制
-
实际工作中,遇到的情况不可能是非常完美的。比如:你写的某个模块,用户输入不一定符合你的要求、你的程序要打开某个文件,这个文件可能不存在或者文件格式不对,你要读取数据库的数据,数据可能是空的等。我们的程序在跑着,内存或硬盘可能满了。。。
-
软件程序在运行过程中,非常可能遇到刚刚提到的这些异常问题,我们叫异常,英文是:Exception,意思是例外。这些,例外情况,或者叫异常,怎么让我们写的程序作出合理的处理。而不至于程序崩溃。
-
异常指程序运行中出现的不期而至的各种情况,如:文件找不到、网络连接失败、非法参数等。
-
异常发生在在程序运行期间,它影响了正常的执行流程。
-
简单的分类:
- 检查性异常:最具代表的检查性异常是用户错误或问题引起的异常,这是程序员无法预见的。例如要打开一个不存在的文件时,一个异常就发生了,这些异常在编译时不能被简单的忽略。
- 运行时异常:运行时异常是可能被程序员避免的异常。与检查性异常相反,运行时异常可以在编译时被忽略。
- 错误ERROR:错误不是异常,而是脱离程序员控制的问题。错误在代码中通常被忽略。例如,当栈溢出时,一个错误就发生了,它们在编译也检查不到的
-
异常体系结构
- java比异常当做对象来处理,并定义一个基类java.lang.Throwable作为所有异常的超类。
- 在java API中已经定义了许多异常类,这些异常类分为两大类,错误Error和异常Exception。[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-OfVbHOz0-1629187619149)(C:\Users\LXK\AppData\Roaming\Typora\typora-user-images\image-20210801104432535.png)]
-
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通常情况下是可以被程序处理的,并且在程序中应该尽可能的去处理这些异常
-
package OOP.Test20; public class Application { public static void main(String[] args) { new Application().a(); } public void a(){ b(); } public void b(){ a(); } }
-
-
-
异常处理机制
-
抛出异常
-
捕获异常
-
异常处理的五个关键字
- try、catch、finally、throw、throws
-
package OOP.Test21.exception; public class Test { public static void main(String[] args) { int a = 1; int b = 0; new Test().test1(1,0); try{ //try监控区域 System.out.println(a/b); }catch (ArithmeticException e){ // catch 捕获异常 System.out.println("程序出现异常,变量b不能为0"); }catch(Exception e){ // catch 捕获异常 System.out.println("Exception"); } finally{ // 处理善后工作(可以不需要) System.out.println("finally"); } // System.out.println(a/b); // ArithmeticException: / by zero } // 假设这个方法中,处理不了这个异常,则可在方法中抛出异常 public void test1(int a,int b){ if(b==0){ // throw throws throw new ArithmeticException();// 主动抛出异常,一般在方法中使用 } } }
-
-
自定义异常
-
使用Java内置的异常类可以描述在编程时出现的大部分异常情况。除此之外,用户还可以自定义异常。用户自定义异常类,只需继承Exception类即可。
-
在程序中使用自定义异常类,大体可以分为以下几个步骤:
- 创建自定义异常类。
- 在方法中通过throw关键字抛出异常对象。
- 如果当前抛出异常的方法中处理异常,可以使用try—catch语句捕获并处理;否则在方法的声明处通过throws关键字指明要抛出给方法调用者的异常,继续进行下一步操作。
- 在出现异常方法的调用者中捕获并处理异常。
-
package OOP.Test22.Exception; // 自 定 义 的 异 常 类 public class MyException extends Exception{ // 传递数字>10 public int detail; public MyException(int a) { this.detail = a; } // toString:异常的打印信息 @Override public String toString() { return "MyException{" + "detail=" + detail + '}'; } }package OOP.Test22.Exception; public class Test { // 可能会存在异常的方法 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(1); }catch (MyException e){ System.out.println("MyException=>"+e); } } }
-
-
实际应用中的经验总结
- 处理运行时异常时,采用逻辑去合理规避同时辅助try—catch处理
- 在多重catch块后面,可以加上一个catch(Exception)来处理可能会被遗漏的异常
- 对于不确定的代码,也可以加上try-catch,处理潜在的异常
- 尽量去处理异常,切忌只是简单地调用printStackTrace()去打印输出
- 具体如何处理异常,要根据不同的业务需求和异常类型去决定
- 尽量添加finally语句块去释放占用的资源
urn “MyException{” +
“detail=” + detail +
‘}’;
}
}
```
```java
package OOP.Test22.Exception;
public class Test {
// 可能会存在异常的方法
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(1);
}catch (MyException e){
System.out.println("MyException=>"+e);
}
}
}
```

浙公网安备 33010602011771号