java —— 面向对象(下)
一、类的继承
(一)继承
子类在继承父类的时候,就会自动拥有父类的所有成员
关键字:extends
格式:子类 extends 父类
class Animal {
String name;
// 定义动物叫的方法
void shout() {
System.out.println("动物发出叫声");
}
}
// 定义 Dog 类继承Animal类
class Dog extends Animal {
// 定义一个打印name的方法
public void printName() {
System.out.println("name=" + name);
}
}
public class Ex01_04 {
public static void main(String[] args) {
Dog dog = new Dog();
dog.name = "沙皮狗";
dog.printName();
dog.shout();
}
}
(二)重写父类方法
class Animal1 {
void shout() {
System.out.println("动物发出叫声");
}
}
class Dog1 extends Animal1 {
// 重写shout()
void shout() {
System.out.println("旺旺");
}
}
public class Ex02_04 {
public static void main(String[] args) {
Dog1 dog1 = new Dog1();
dog1.shout();
}
}
(三)super关键字
当子类重写父类的方法后,子类对象将无法访问父类被重写的方法,我们需要用到super关键字
1、案例一(调用父类)
class Animal2 {
String name = "动物";
// 定义动物叫的方法
void shout() {
System.out.println("动物发出叫声:");
}
}
// 定义Dog 类继承动物类
class Dog2 extends Animal2 {
String name = "狗";
// 重写父的shout()方法
void shout() {
super.shout(); // 访问父的成员方法
}
// 定义定义name分方法
void printName() {
System.out.println("name=" + super.name); // 访问父的成员变量
}
}
// 定义测试类
public class Ex03_04 {
public static void main(String[] args) {
Dog2 dog2 = new Dog2();
dog2.shout(); // 到子类
dog2.printName();
}
}
2、案例二(子类传参到父类)
class Animal3 {
// 定义Animal 类有参的构造方法
public Animal3(String name) {
System.out.println("我是一只" + name);
}
}
// 定义Dog3 类继承动物类
class Dog3 extends Animal3 {
public Dog3(){
super("小够");
}
}
// 定义测试类
public class Ex04_04 {
public static void main(String[] args) {
Dog3 dog3 = new Dog3(); // 实例化对象,会自动调用Dog3的方法
}
}
3、案例三(给父类一个无参,避免报错)
class Animal4 {
// 定义Animal 类无参的构造方法
public Animal4() {
System.out.println("我是一只动物");
}
// 定义Animal 类有参的构造方法
public Animal4(String name) {
System.out.println("我是一只" + name);
}
}
// 定义Dog4 类继承动物类
class Dog4 extends Animal4 {
public Dog4() {
// 当没有任何的调用或者输出,程序可能会报错,如果我们给父类一个无参输出,就不会报错
}
}
// 定义测试类
public class Ex05_04 {
public static void main(String[] args) {
Dog4 dog4 = new Dog4(); // 实例化对象,会自动调用Dog4的方法
}
}
二、final关键字
- final修饰的类不能被继承
- final修饰的方法不能班子类重写
- final修饰的变量(成员变量和局部变量)是常量,只能赋值一次
(一)修饰类
当父类使用了final关键字,子类Dog5无法继承Animal5
(二)修饰方法
无法重写方法
(三)修饰变量
无法重现赋值
(四)案例
1、不给name任何赋值,实际会是null
class Student{
String name;
// 打印学生
public void introduce(){
System.out.println("我是一个学生,我叫"+name);
}
}
public class Ex09_04 {
public static void main(String[] args) {
Student stu = new Student();
stu.introduce();
}
}
2、使用final,程序会报错,因为没有任何的返回值
3、正确做法:
class Student{
final String name = "小白";
// 打印学生
public void introduce(){
System.out.println("我是一个学生,我叫"+name);
}
}
public class Ex09_04 {
public static void main(String[] args) {
Student stu = new Student();
stu.introduce();
}
}
三、抽象类
例如前面的Animal类或者shout方法,一个是动物,一个是动物的叫声,动物是什么动物?动物的叫声又是什么声音?我们无法想象。抽象方法我们必须使用abstract关键字来修饰。
抽象方法的类必须声明为抽象类,但抽象类可以不包含任何抽象方法
abstract class Animal7{
abstract void shout();
}
class Dog7 extends Animal7{
void shout(){
System.out.println("白白");
}
}
public class Ex10_04 {
public static void main(String[] args) {
Dog7 dog7 = new Dog7();
dog7.shout();
}
}
四、接口
- 解释:Java 中类只能单继承,但一个类可以实现多个接口。通过使用接口,可以让一个类同时具有多个不同类型的行为,达到类似多继承的效果。
- 示例:假设有一个
Robot
类,它既需要像Worker
一样工作,又需要像Singer
一样唱歌。可以定义Worker
和Singer
接口,然后让Robot
类实现这两个接口。
(一)案例一
interface:接口 implements:实现
// Dog8类实现了Animal8接口
class Dog8 implements Animal8{
// 实现breathe()方法
public void breathe(){
System.out.println("狗在呼吸");
}
// 实现run()方法
public void run(){
System.out.println("狗在跑");
}
}
public class Ex11_04 {
public static void main(String[] args) {
Dog8 dog8 = new Dog8();
dog8.breathe();
dog8.run();
}
}
(二)案例二
接口继承接口,先继承再实现
class Dog9 implements LandAnimal9 {
// 实现breathe()方法
public void breathe() {
System.out.println("狗在呼吸");
}
// 实现run()方法
public void run() {
System.out.println("狗在跑");
}
public void liveOnland() {
// TODO Auto-generated method stub
System.out.println("狗生活在陆地上");
}
}
public class Ex12_04 {
public static void main(String[] args) {
Dog9 dog9 = new Dog9();
dog9.breathe();
dog9.run();
dog9.liveOnland();
}
}
TODO
:这是一个常见的注释标记,一般用于提醒开发者在后续需要完成某些任务或者进行代码完善。当 IDE(集成开发环境,像 IntelliJ IDEA、Eclipse 等)搜索代码中的TODO
标记时,会把这些标记相关的注释显示出来,方便开发者集中处理待办事项。Auto-generated method stub
:意思是 “自动生成的方法存根”。在使用 IDE 的自动生成功能(例如实现接口方法、重写父类方法等)时,IDE 会自动创建方法的基本框架,也就是方法签名和空的方法体,同时添加这个注释来表明这是自动生成的代码,还需要开发者去完善具体的实现逻辑。
五、多态
(一)案例一
1、创建好接口
2、实现对口
class Cat10 implements Animal10 {
public void shout() {
System.out.println("喵喵~~");
}
}
class Dog10 implements Animal10 {
public void shout() {
System.out.println("旺旺~~");
}
}
public class Ex13_04 {
public static void main(String[] args) {
Animal10 animal1 = new Cat10();
Animal10 animal2 = new Dog10();
animalShout(animal1);
animalShout(animal2);
}
public static void animalShout(Animal10 an) {
an.shout();
}
}
3、解析
引用类型不同
-
Cat10 animal1 = new Cat10();
:-
引用类型为
Cat10
,即animal1
这个引用变量被明确指定为Cat10
类型。这意味着animal1
可以直接访问Cat10
类中定义的所有方法和属性,包括Cat10
类特有的方法和属性,以及从父类继承过来的方法和属性。
-
-
Animal10 animal1 = new Cat10();
:-
引用类型为
Animal10
,这里体现了 Java 的多态特性。animal1
是一个Animal10
类型的引用变量,但它指向的是一个Cat10
类的对象。在编译阶段,编译器会根据引用类型Animal10
来检查可以调用的方法和属性,所以animal1
只能访问Animal10
类中定义的方法和属性(包括被Cat10
类重写的方法),而不能直接访问Cat10
类特有的方法和属性。
-
(二)案例二
instanceof:用于检查一个对象是否是某个特定类、接口的实例,或者是否是其子类、实现类的实例
class Cat11 implements Animal10 {
public void shout() {
System.out.println("喵喵~~");
}
void sleep() {
System.out.println("猫睡觉---");
}
}
class Dog11 implements Animal10 {
public void shout() {
System.out.println("旺旺~~");
}
}
public class Ex14_04 {
public static void main(String[] args) {
Dog11 dog11 = new Dog11();
animalShout(dog11);
}
public static void animalShout(Animal10 an) {
if (an instanceof Cat11) {
Cat11 cat11 = (Cat11) an;
cat11.shout();
cat11.sleep();
} else {
System.out.println("不是Cat11");
}
}
}
Animal10 an
:方法的参数,Animal10
是一个接口类型,an
是传入的参数名。这意味着该方法可以接受任何实现了Animal10
接口的对象作为参数,体现了多态性。- animalShout函数这里的作用是检查传入的
an
对象是否是Cat11
类的实例。如果是,则执行if
语句块中的代码;如果不是,则跳过该语句块。
(三)案例三
class Cat11 implements Animal10 {
public void shout() {
System.out.println("喵喵~~");
}
void sleep() {
System.out.println("猫睡觉---");
}
}
class Dog11 implements Animal10 {
public void shout() {
System.out.println("旺旺~~");
}
}
public class Ex14_04 {
public static void main(String[] args) {
Dog11 dog11 = new Dog11();
animalShout(dog11);
}
public static void animalShout(Animal10 an) {
if (an instanceof Cat11) {
Cat11 cat11 = (Cat11) an;
cat11.shout();
cat11.sleep();
} else if (an instanceof Dog11) {
Dog11 dog11 = (Dog11) an;
dog11.shout();
} else {
System.out.println("类型不是Cat11");
}
}
}
(四)Object类
- 在 Java 中,
toString
是Object
类中定义的一个方法,所有的类都继承自Object
类,因此所有的 Java 类都拥有toString
方法。 toString
方法的主要作用是返回一个对象的字符串表示形式。这个字符串通常用于调试、日志记录或者将对象信息以文本形式呈现给用户。当你直接打印一个对象或者将对象与字符串进行拼接时,Java 会自动调用该对象的toString
方法。
1、实际的toString
class Animal11{
// 重写Object类的toString()方法
public String dd(){
return "I am an animal";
}
}
public class Ex17_04 {
public static void main(String[] args) {
Animal11 animal11 = new Animal11();
System.out.println(animal11.toString());
}
}
- 前面是类名
- “b4c966a”:这是对象哈希码(hash code)的十六进制表示 。在 Java 里,每个对象都有哈希码,默认由
Object
类的hashCode
方法生成。这是toString
方法默认实现的输出形式。因为没有在Animal11
类中重写toString
方法,所以直接打印对象时,就会按照Object
类中toString
方法的默认逻辑,显示类名加哈希码十六进制值。
2、重写Object的toString()方法
class Animal11 {
// 重写Object类的toString()方法
public String toString() {
return "I am an animal";
}
}
public class Ex17_04 {
public static void main(String[] args) {
Animal11 animal11 = new Animal11();
System.out.println(animal11.toString());
}
}
综合题(PCI)
// 网卡
class NetWorkCard implements PCI {
public void start() {
System.out.println("send。。。");
}
public void stop() {
System.out.println("NetWord stop");
}
}
// 声卡
class SoundCard implements PCI {
public void start() {
System.out.println("dudu");
}
public void stop() {
System.out.println("sound stop");
}
}
// 主板(只需要接收和调用)
class MainBoard {
public void userPCICard(PCI p) {
p.start();
p.stop();
}
}
// 执行者
public class Assembler {
public static void main(String[] args) {
MainBoard mb = new MainBoard();
NetWorkCard nc = new NetWorkCard();
mb.userPCICard(nc);
SoundCard sc = new SoundCard();
mb.userPCICard(sc);
}
}
start
方法:当网卡启动时,输出"send。。。"
,模拟网卡发送数据的操作。stop
方法:当网卡停止时,输出"NetWord stop"
,表示网卡停止工作。
六、异常
在 Java 中,Throwable
是所有错误和异常的超类,它有一个层次清晰的类继承结构,以下是用文字形式描述的Throwable:
Error
:表示严重的系统错误,通常是 Java 虚拟机内部错误或资源耗尽等情况,应用程序一般不应该捕获和处理这类错误,比如OutOfMemoryError
(内存溢出错误)、StackOverflowError
(栈溢出错误)等。Exception
:表示程序运行中可能出现的异常情况,又可进一步分为受检异常(Checked Exception)和非受检异常(Unchecked Exception)。—— 一般都是这种
(一)try异常,try终止
getMessage()
方法返回异常的描述信息
public class Ex21_04 {
public static void main(String[] args) {
try {
int result = divide(4, 0);
System.out.println(result);
} catch (Exception e) {
System.out.println("异常信息为:" + e.getMessage());
}
System.out.println("继续");
}
public static int divide(int x, int y) {
return x / y;
}
}
(二)try异常,继续执行
public class E22_04 {
public static void main(String[] args) {
try {
int result = divide(4, 0);
System.out.println(result);
} catch (Exception e) {
System.out.println("异常信息为:" + e.getMessage());
} finally {
System.out.println("进入finally代码块");
}
System.out.println("继续");
}
public static int divide(int x, int y) {
return x / y;
}
}
(三)throws关键字
使用场景
- 受检异常处理:对于受检异常(即继承自
Exception
但不继承自RuntimeException
(RuntimeException
是Exception
类的子类)的异常),Java 要求必须对其进行处理,要么使用try-catch
块捕获,要么使用throws
关键字声明抛出。当方法内部可能抛出受检异常,而方法本身不想处理时,就可以使用throws
声明抛出该异常,让调用者处理。 - 异常责任传递:在大型项目中,为了遵循单一职责原则,一个方法可能只负责完成特定的任务,而将异常处理的责任交给调用者,此时可以使用
throws
关键字将异常抛给调用者。
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
class ThrowsExample2 {
// 声明该方法可能会抛出 FileNotFoundException 和 IOException 异常
public static void readFile(String filePath) throws FileNotFoundException, IOException {
FileInputStream fis = new FileInputStream(filePath);
int data = fis.read();
while (data != -1) {
// 处理数据
data = fis.read();
}
fis.close();
}
public static void main(String[] args) {
try {
readFile("nonexistentfile.txt");
} catch (FileNotFoundException e) {
System.out.println("文件未找到:" + e.getMessage());
} catch (IOException e) {
System.out.println("读取文件时发生 I/O 错误:" + e.getMessage());
}
}
}
七、包
在 Java 里,包(Package)是一种组织类和接口的机制,它能将相关的类和接口放在一起,方便代码的管理、维护和复用。下面从包的概念、作用、定义与使用、命名规范等方面详细介绍。
(一)概念
包是一种命名空间,它把类和接口按照一定的逻辑结构进行分组。就好比文件系统中的文件夹,用于存放不同类型的文件,包则用于存放不同功能的类和接口。
(二)作用
- 避免命名冲突:在大型项目中,可能会有很多类和接口。如果没有包的组织,不同开发者或不同模块定义的类名可能会重复,从而引发命名冲突。使用包可以将类和接口分隔在不同的命名空间中,避免这种冲突。例如,不同公司开发的同名类可以放在不同的包中。
- 提高代码的可维护性:将相关的类和接口放在同一个包中,使得代码结构更加清晰,便于开发者查找和管理。比如,将所有与数据库操作相关的类放在一个名为
com.example.database
的包中,将所有与用户界面相关的类放在com.example.ui
包中。 - 控制访问权限:包可以和访问修饰符(如
private
、protected
、public
)配合使用,实现对类和接口的访问控制。例如,使用protected
修饰的成员可以被同一个包中的其他类访问。
(三)创建包和导入包
* :通配符(所有的方法),如果单一调用某个方法,则后面使用方法名
(四)命名规范
Java 包名通常采用小写字母,并且遵循域名反转的规则。例如,一个公司的域名为 example.com
,那么该公司开发的 Java 项目的包名通常以 com.example
开头。这样做的好处是可以确保包名的全球唯一性,避免命名冲突。以下是一些常见的包名示例:
java.lang
:Java 语言的核心类库所在的包。java.util
:包含了很多实用工具类和集合类的包。com.google.gson
:Google 开发的 Gson 库的包名。
(五)包的目录结构
在文件系统中,包的结构对应着目录结构。例如,包 com.example.util
对应的目录结构为 com/example/util(可在电脑文件查看)
刚刚创建包的位置: