面向对象的思想是一种程序设计的方法论,它将程序的设计和实现分解为对象的创建、操作和交互。面向对象的思想将现实世界中的事物抽象为对象,每个对象包含属性(数据)和方法(行为),并且具有封装、继承和多态的特性。
面向对象的思想强调将程序划分为独立的对象,每个对象负责自己的任务,对象之间通过消息传递的方式交互。这种方式可以提高代码的可维护性、可复用性和可扩展性,使程序更易于理解和修改。
面向对象的思想还提供了一种抽象的方法,通过定义类来描述对象的共同特征和行为,从而实现代码的模块化和组件化。通过继承机制,可以创建新的类并从现有类继承属性和方法,从而减少代码的重复和冗余。
面向对象的特点主要有以下几点:
-
封装:将数据和对数据的操作封装在一起,形成一个类,外部只能通过类的接口来访问和操作数据,从而实现了数据的隐藏和保护。
-
继承:通过继承,子类可以继承父类的属性和方法,从而减少代码的重复性。并且通过继承,可以实现多态性,即同一个方法可以根据不同的对象调用不同的实现。
-
多态:同一种类型的对象,执行同一个方法时,可以有不同的行为表现,这就是多态性。多态性可以提高代码的灵活性和可扩展性。
-
抽象:通过抽象,可以将一类对象的共同特征和行为抽象出来,形成类的继承层次结构。抽象可以隐藏对象的具体实现细节,只对外提供接口,从而降低系统的复杂性。
-
接口:接口定义了对象与外界之间的通信协议。通过接口,可以实现类之间的松耦合,增加代码的可维护性和可扩展性。
总结来说,面向对象的思想是一种以对象为中心的程序设计方法,通过将程序分解为独立的对象来实现代码的可维护性、可复用性和可扩展性。它是现代软件开发中广泛使用的一种编程范式。
目录
封装
在面向对象程式设计方法中,封装(英语:Encapsulation)是指一种将抽象性函式接口的实现细节部分包装、隐藏起来的方法。
封装可以被认为是一个保护屏障,防止该类的代码和数据被外部类定义的代码随机访问。
要访问该类的代码和数据,必须通过严格的接口控制。
封装最主要的功能在于我们能修改自己的实现代码,而不用修改那些调用我们代码的程序片段。
适当的封装可以让程式码更容易理解与维护,也加强了程式码的安全性。
封装的优点
-
隐藏实现细节
-
提高代码的可维护性
-
提供接口
-
实现数据封装和保护
实现封装
Java通过访问修饰符来实现封装,主要有public、private和protected三种修饰符。
-
public修饰符:使用public修饰的成员变量和成员方法可以被任何类访问。
-
private修饰符:使用private修饰的成员变量和成员方法只能被自身类中的其他成员访问,其他类无法直接访问。
-
protected修饰符:使用protected修饰的成员变量和成员方法可以被自身类、同一包内的其他类以及不同包中的子类访问。
封装还可以通过getter和setter方法来实现。
- getter方法:用于获取私有成员变量的值,一般以get开头,返回该成员变量的值。
private String name;
public String getName() {
return name;
}
- setter方法:用于设置私有成员变量的值,一般以set开头,接收一个参数,将参数的值赋给该成员变量。
private int age;
public void setAge(int age) {
this.age = age;
}
通过getter和setter方法,可以在类的外部访问私有成员变量,同时可以对成员变量的值进行控制和验证。这种方式更加安全和灵活,也符合面向对象编程的封装特性。
案例
题目1
定义一个类Person,定义name和age私有属性,定义有参的构造方法对name和age进行初始化。在测试类中创建该类的2个对象,姓名、年龄分别为lili、19和lucy、20,在屏幕打印出2个对象的姓名和年龄。
代码
package DEMO230201.encapsulationdemo;
class Person {
private String name;//私有属性
private int age;//私有属性
public Person(String name, int age){//定义有参的构造方法
this.name=name;
this.age=age;
}
public void read(){ //在屏幕打印出对象的姓名和年龄的方法
System.out.println("姓名:"+name+",年龄:"+age);
}
}
public class encapsulation1 {
public static void main(String[] args){
Person p1=new Person("lili",19);
Person p2=new Person("lucy",20);
p1.read();
p2.read();
}
}
运行结果
姓名:lili,年龄:19
姓名:lucy,年龄:20
题目2
首先定义一个计算长方形面积的类rectangle,要求类中有一个定义长方形左上角和右下角座标的构造函数,以及一个通过长方形右下角座标与左上角座标计算长方形面积,并实例化两个长方形进行测试。
代码
package DEMO230201.encapsulationdemo;
class Rectangle{
int topLeftX; //左上的x坐标
int topLeftY; //左上的y坐标
int bottomRightX; //右下的x坐标
int bottomRightY; //右下的y坐标
public Rectangle(int topLeftX,int topLeftY,int bottomRightX,int bottomRightY){
this.topLeftX=topLeftX;
this.topLeftY=topLeftY;
this.bottomRightX=bottomRightX;
this.bottomRightY=bottomRightY;
}
public void area(){ //计算长方形面积的方法
int len=bottomRightX-topLeftX;
int wid=topLeftY-bottomRightY;
System.out.println("长方形面积为:"+len*wid);
}
}
public class encapsulation2 {
public static void main(String[] args) {
Rectangle r1=new Rectangle(1,8,2,6); //实例化第一个例子
r1.area();
Rectangle r2=new Rectangle(0,2,2,0); //实例化第二个例子
r2.area();
}
}
运行结果
长方形面积为:2
长方形面积为:4
题目3
设计一个表示图书的Book类,它包含图书的书名、作者、月销售量等私有属性,另有两个构造方法(一个不带参数,另一个带参数),成员方法setBook( ) 和printBook()分别用于设置和输出书名、作者、月销售量等数据。并设计相应的测试Book类的应用程序主类,测试并显示输出提供所有功能的结果。
代码
package DEMO230201.encapsulationdemo;
class Book{
private String title;
private String author;
private int monthlySales;
public Book(){ //无参构造
}
public Book(String title,String author,int monthlySales){ //有参构造
this.title=title;
this.author=author;
this.monthlySales=monthlySales;
}
public void setTitle(String title){
this.title=title;
}
public void setAuthor(String author){
this.author=author;
}
public void setMonthlySales(int monthlySales){
this.monthlySales=monthlySales;
}
public void printBook(){
System.out.println("本书的书名:"+title+",作者:"+author+",月销售量:"+monthlySales);
}
}
public class encapsulation3 {
public static void main(String[] args){
Book b1=new Book();
b1.setTitle("尘埃落定");
b1.setAuthor("阿来");
b1.setMonthlySales(100000);
b1.printBook();
Book b2=new Book("命运","蔡崇达",200000);
b2.printBook();
}
}
运行结果
本书的书名:尘埃落定,作者:阿来,月销售量:100000
本书的书名:命运,作者:蔡崇达,月销售量:200000
题目4
请创建一个银行帐户类,要求如下:(1)类包括帐户名、帐户号、存款额等私有属性;(3)有三个参数的构造方法(2)可实现余额查询,存款和取款的操作。(3)创建该类的对象,验证以上两项。
代码
package DEMO230201.encapsulationdemo;
class BankAccount{
private String accountName;
private String accountNumber;
private int depositAmount;
public BankAccount(String accountName,String accountNumber,int depositAmount){
this.accountName=accountName;
this.accountNumber=accountNumber;
this.depositAmount=depositAmount;
}
public void balanceInquiry(){ //余额查询
System.out.println("您好!您的帐户名:"+accountName+",您的帐户号:"+accountNumber+",您当前余额还剩:"+depositAmount);
}
public void deposit(int money){ //存款
depositAmount=depositAmount+money;
System.out.println("存款成功,当前余额还剩:"+depositAmount);
}
public void Withdrawal(int money){ //取款
depositAmount=depositAmount-money;
System.out.println("取款成功,当前余额还剩:"+depositAmount);
}
}
public class encapsulation4 {
public static void main(String[] args){
BankAccount b1=new BankAccount("R","1818181818",1000000);
b1.balanceInquiry();
b1.deposit(100000);
b1.Withdrawal(1000);
}
}
运行结果
您好!您的帐户名:R,您的帐户号:1818181818,您当前余额还剩:1000000
存款成功,当前余额还剩:1100000
取款成功,当前余额还剩:1099000
继承
在JAVA中,类的继承是指在一个现有类的基础上去构建一个新的类,构建出来的新类称为子类,现有类称为父类。子类继承父类的属性和方法,使得子类对象(实例)具有父类的特征和行为。
特征
(1)继承关系是传递的。若类C继承类B,类B继承类A(多层继承),则类C既有从类B那里继承下来的属性与方法,也有从类A那里继承下来的属性与方法,还可以有自己新定义的属性和方法。继承来的属性和方法尽管是隐式的,但仍是类C的属性和方法。继承是在一些比较一般的类的基础上构造、建立和扩充新类的最有效的手段。
(2)继承简化了人们对事物的认识和描述,能清晰体现相关类间的层次结构关系。
(3)继承提供了软件复用功能。(复用性主要是可以多次使用,不用再多次写同样的代码)若类B继承类A,那么建立类B时只需要再描述与基类(类A)不同的少量特征(数据成员和成员方法)即可。
(4)继承通过增强一致性来减少模块间的接口和界面,大大增加了程序的易维护性。
(5)提供多重继承机制。从理论上说,一个类可以是多个一般类的特殊类,它可以从多个一般类中继承属性与方法,这便是多重继承。Java出于安全性和可靠性的考虑,仅支持单重继承,而通过使用接口机制来实现多重继承。
实现继承
在 Java 中通过 extends 关键字可以申明一个类是从另外一个类继承而来的,一般形式如下:
class 父类 {
... // 父类的属性和方法
}
class 子类 extends 父类 {
... // 子类的属性和方法
}
需要注意的是 Java 不支持多继承,但支持多重继承。
继承关键字
extends关键字
在 Java 中,类的继承是单一继承,也就是说,一个子类只能拥有一个父类,所以 extends 只能继承一个类。
implements关键字
使用 implements 关键字可以变相的使java具有多继承的特性,使用范围为类继承接口的情况,可以同时继承多个接口(接口跟接口之间采用逗号分隔)。
super 与 this 关键字
super关键字:我们可以通过super关键字来实现对父类成员的访问,用来引用当前对象的父类。
(super调用父类构造时,一定要放在构造方法的首行上,且只能出现一次)
super.成员变量
super.成员方法(参数1,参数2,...)
this关键字:指向自己的引用。
this.成员变量 //调用本类中的属性
this.成员方法(参数1,参数2,...) //调用成员方法
this() //调用本类中的构造方法
在使用this调用类的构造方法时,注意:
- 只能在构造方法中使用this调用其他构造方法,不能再成员方法中通过this调用其他构造方法
- 在构造方法中,使用this调用构造方法的语句必须位于第一行,且只能出现一次
- 不能在一个类中的两个构造方法中使用this互相调用
final 关键字
final 可以用来修饰变量(包括类属性、对象属性、局部变量和形参)、方法(包括类方法和对象方法)和类。
final 含义为 "最终的"。
使用 final 关键字声明类,就是把类定义定义为最终类,不能被继承,或者用于修饰方法,该方法不能被子类重写:
-
声明类:
final class 类名 {//类体}
-
声明方法:
修饰符(public/private/default/protected) final 返回值类型 方法名(){//方法体}
注: final 定义的类,其中的属性、方法不是 final 的。
Object类
JAVA提供了一个Object类,它是所有类的父类,每个类都直接或间接地继承Object类,因此Object类通常称为超类。Object 类位于 java.lang 包中,编译时会自动导入,我们创建一个类时,如果没有明确继承一个父类,那么它就会自动继承 Object,成为 Object 的子类。
Object 类可以显式继承,也可以隐式继承,以下两种方式是一样的:
显式继承:
public class Runoob extends Object{
}
隐式继承:
public class Runoob {
}
Object类的常用方法
方法 & 描述 |
---|
boolean equals(Object obj) 比较两个对象是否相等 |
String toString() 返回对象的字符串表示形式 |
案例
题目1
设计一个学生类Student,其数据成员有name(姓名)、age(年龄)和degree(学位)。由Student类派生出本科生类Undergraduate和研究生类Graduate,本科生类Undergraduate增加成员specialty(专业),研究生类增加成员direction(研究方向)。每个类都有show()方法,用于输出数据成员信息。最后请输出下列信息:
代码
package DEMO230201.inheritdemo;
class Student{ //父类Student
String name;
int age;
String degree;
public Student(String name,int age,String degree){
super();
this.name=name;
this.age=age;
this.degree=degree;
}
public void show(){
System.out.printf("姓名:"+name+" 年龄:"+age+" 学位"+degree);
}
}
class Undergraduate extends Student{ //子类Undergraduate父类Student
String specialty;
public Undergraduate(String name,int age,String degree,String specialty){
super(name,age,degree);
this.specialty=specialty;
}
public void show(){
super.show();
System.out.println(" 专业:"+specialty);
}
}
class Graduate extends Student{ 子类Graduate父类Student
String direction;
public Graduate(String name,int age,String degree,String direction){
super(name,age,degree);
this.direction=direction;
}
public void show(){
super.show();
System.out.println(" 研究方向:"+direction);
}
}
public class inherit1 {
public static void main(String[] args){
Undergraduate u1=new Undergraduate("张三",20,"本科","通信");
Undergraduate u2=new Undergraduate("李四",21,"本科","电子");
Graduate g1=new Graduate("王五",25,"硕士","通信");
Graduate g2=new Graduate("刘六",36,"博士","通信");
u1.show();
u2.show();
g1.show();
g2.show();
}
}
运行结果
姓名:张三 年龄:20 学位本科 专业:通信
姓名:李四 年龄:21 学位本科 专业:电子
姓名:王五 年龄:25 学位硕士 研究方向:通信
姓名:刘六 年龄:36 学位博士 研究方向:通信
题目2
有工人、服务员、教师、科学家四种角色,其中服务员、工人只有月固定工资(元/月),教师除月固定工资外,还有课酬(元/节)。科学家除月固定工资外,还有科研激励奖(元/季度)。请通过继承设计出相应的类,将各种类型的员工的全年工资打印出来,并测试(张三、工人、4000元/月)、(李四、服务员、3500元/月)、(王五、教师、5000元/月、100元/节,200节/年)、(刘六、科学家、7000元/月、20000元/季度)。
代码
package DEMO230201.inheritdemo;
class Occupation{
String name;
String job;
int monthlySalary;
public Occupation(String name,String job,int monthlySalary){
super();
this.name=name;
this.job=job;
this.monthlySalary=monthlySalary;
}
}
class WaitersWorkers extends Occupation{
public WaitersWorkers(String name,String job,int monthlySalary) {
super(name,job,monthlySalary);
}
public void printSalary(){
int yearlySalary=super.monthlySalary*12;
System.out.println(super.job+super.name+"的全年工资为:"+yearlySalary);
}
}
class Teacher extends Occupation{
int remuneration;//课酬
int number;//课时
public Teacher(String name,String job,int monthlySalary,int remuneration,int number){
super(name,job,monthlySalary);
this.remuneration=remuneration;
this.number=number;
}
public void printSalary(){
int yearlySalary=super.monthlySalary*12+remuneration*number;
System.out.println(super.job+super.name+"的全年工资为:"+yearlySalary);
}
}
class Scientist extends Occupation{
int researchIncentiveAward;
public Scientist(String name,String job,int monthlySalary,int researchIncentiveAward){
super(name,job,monthlySalary);
this.researchIncentiveAward=researchIncentiveAward;
}
public void printSalary(){
int yearlySalary=super.monthlySalary*12+researchIncentiveAward*4;
System.out.println(super.job+super.name+"的全年工资为:"+yearlySalary);
}
}
public class inherit2 {
public static void main(String[] args){
WaitersWorkers w1=new WaitersWorkers("张三","工人",4000);
WaitersWorkers w2=new WaitersWorkers("李四","服务员",3500);
Teacher t1=new Teacher("王五","教师",5000,100,200);
Scientist s1=new Scientist("刘六","科学家",7000,20000);
w1.printSalary();
w2.printSalary();
t1.printSalary();
s1.printSalary();
}
}
运行结果
工人张三的全年工资为:48000
服务员李四的全年工资为:42000
教师王五的全年工资为:80000
科学家刘六的全年工资为:164000
题目3
根据下述继承关系图,请完成父类Pet 、子类Dog、Penguin的设计,并写一个测试类,测试类中完成狗对象的创建与输出,企鹅对象的创建与输出。
代码
package DEMO230201.inheritdemo;
class Pet{
String name;
int health;
int love;
public Pet(String name,int health,int love){
super();
this.name=name;
this.health=health;
this.love=love;
}
public String getName(){
return name;
}
public int getHealth(){
return health;
}
public int getLove(){
return love;
}
public void printPet(){
System.out.print("My name is "+getName()+",my health is "+getHealth()+",my love is "+getLove());
}
}
class Dog extends Pet{
String strain;
public Dog(String name,int health,int love,String strain){
super(name,health,love);
this.strain=strain;
}
public String getStrain(){
return strain;
}
public void printPet(){
super.printPet();
System.out.println(",my strain is "+getStrain());
}
}
class Penguin extends Pet{
String sex;
public Penguin(String name,int health,int love,String sex){
super(name,health,love);
this.sex=sex;
}
public String getSex(){
return sex;
}
public void printPet(){
super.printPet();
System.out.println(",my sex is "+getSex());
}
}
public class inherit3 {
public static void main(String[] args){
Dog d1=new Dog("A",100,20,"牧羊犬");
Penguin p1=new Penguin("Q仔",100,20,"female");
d1.printPet();
p1.printPet();
}
}
运行结果
My name is A,my health is 100,my love is 20,my strain is 牧羊犬
My name is Q仔,my health is 100,my love is 20,my sex is female
抽象类
当定义一个类是时,常常需要定义一些成员方法描述类的行为特征,但有时这些方法的实现是无法确定的。针对这种情况,JAVA提供了抽象方法来满足需求。
在面向对象的概念中,所有的对象都是通过类来描绘的,但是反过来,并不是所有的类都是用来描绘对象的,如果一个类中没有包含足够的信息来描绘一个具体的对象,这样的类就是抽象类。
抽象类除了不能实例化对象之外,类的其它功能依然存在,成员变量、成员方法和构造方法的访问方式和普通类一样。
由于抽象类不能实例化对象,所以抽象类必须被继承,才能被使用。也是因为这个原因,通常在设计阶段决定要不要设计抽象类。
父类包含了子类集合的常见的方法,但是由于父类本身是抽象的,所以不能使用这些方法。
在 Java 中抽象类表示的是一种继承关系,一个类只能继承一个抽象类,而一个类却可以实现多个接口。
实现抽象类
如果你想设计这样一个类,该类包含一个特别的成员方法,该方法的具体实现由它的子类确定,那么你可以在父类中声明该方法为抽象方法。
Abstract 关键字同样可以用来声明抽象方法,抽象方法只包含一个方法名,而没有方法体。
抽象方法没有定义,方法名后面直接跟一个分号,而不是花括号。
abstract class 抽象类名称 {
属性;
访问权限 返回值类型 方法名称 (参数){ //普通方法
return [返回值];
}
访问权限 abstract 返回值类型 抽象方法名称 (参数); ///抽象方法,无方法体
}
声明抽象方法会造成以下两个结果:
- 如果一个类包含抽象方法,那么该类必须是抽象类。
- 任何子类必须重写父类的抽象方法,或者声明自身为抽象类。
继承抽象方法的子类必须重写该方法。否则,该子类也必须声明为抽象类。最终,必须有子类实现该抽象方法,否则,从最初的父类到最终的子类都不能用来实例化对象。
规定
-
抽象方法必须为public或者protected(因为如果为private,则不能被子类继承,子类便无法实现该方法),缺省情况下默认为public;
-
抽象类不能被实例化(初学者很容易犯的错),如果被实例化,就会报错,编译无法通过。只有抽象类的非抽象子类可以创建对象。
-
抽象类中不一定包含抽象方法,但是有抽象方法的类必定是抽象类。
-
抽象类中的抽象方法只是声明,不包含方法体,就是不给出方法的具体实现也就是方法的具体功能。
-
构造方法,类方法(用 static 修饰的方法)不能声明为抽象方法。
-
抽象类的子类必须给出抽象类中的抽象方法的具体实现,除非该子类也是抽象类。
案例
题目1
首先设计一个学生抽象类Student,其数据成员有name(姓名)、age(年龄)和degree(学位),以及一个抽象方法show()。然后由Student类派生出本科生类Undergraduate和研究生类Graduate,本科生类Undergraduate增加成员specialty(专业),研究生类增加成员direction(研究方向)。并且每个类都有show()方法,用于输出数据成员信息。请定义对象,并打印输出下列信息:
张三:20,本科,计算机科学
李四:21,本科,物联网
王五:25,硕士,软件工程
刘六:36,博士,通信工程
代码
package DEMO230201.abstractdemo;
abstract class Student{
String name;
int age;
String degree;
public Student(String name,int age,String degree){
super();
this.name=name;
this.age=age;
this.degree=degree;
}
public abstract void show();
}
class Undergraduate extends Student {
String specialty;
public Undergraduate(String name,int age,String degree,String specialty){
super(name,age,degree);
this.specialty=specialty;
}
public void show(){
System.out.println(super.name+" :"+super.age+","+super.degree+","+specialty);
}
}
class Graduate extends Student {
String direction;
public Graduate(String name,int age,String degree,String direction){
super(name,age,degree);
this.direction=direction;
}
public void show(){
System.out.println(super.name+" :"+super.age+","+super.degree+","+direction);
}
}
public class abstract1 {
public static void main(String[] args){
Undergraduate u1=new Undergraduate("张三",20,"本科","计算机科学");
Undergraduate u2=new Undergraduate("李四",21,"本科","物联网");
Graduate g1=new Graduate("王五",25,"硕士","软件工程");
Graduate g2=new Graduate("刘六",36,"博士","通信工程");
u1.show();
u2.show();
g1.show();
g2.show();
}
}
运行结果
张三 :20,本科,计算机科学
李四 :21,本科,物联网
王五 :25,硕士,软件工程
刘六 :36,博士,通信工程
题目2
设计一个抽象类Graphics,它具有一个String类型参数name和两个抽象方法parameter()、area(),name用来存储图形的名称,parameter()方法用于输出图形的名称和其它属性特征,area()方法用于输出图形的面积。请用该类派生的子类实现输出一个形状为长方形、长为3宽为2和它面积以及输出一个形状为圆形、颜色为红色、半径为4和它面积。
代码
package DEMO230201.abstractdemo;
abstract class Graphics{
String name;
public Graphics(String name){
super();
this.name=name;
}
public abstract void parameter();
public abstract void area();
}
class Rectangle extends Graphics{
int length;
int width;
public Rectangle(String name,int length,int width){
super(name);
this.length=length;
this.width=width;
}
public void parameter(){
System.out.println("图形属性为:"+name+",图形的长为:"+length+",宽为:"+width);
}
public void area(){
System.out.println("图形面积为:"+length*width);
}
}
class Rotundity extends Graphics{
String color;
int radius;
public Rotundity(String name,String color,int radius){
super(name);
this.color=color;
this.radius=radius;
}
public void parameter(){
System.out.println("图形属性为:"+name+",图形的颜色为:"+color+",图形的半径为:"+radius);
}
public void area(){
System.out.println("图形面积为:"+3.14*radius*radius);
}
}
public class abstract2 {
public static void main(String[] args){
Rectangle re1=new Rectangle("长方形",3,2);
Rotundity ro1=new Rotundity("圆形","红色",4);
re1.parameter();
re1.area();
ro1.parameter();
ro1.area();
}
}
运行结果
图形属性为:长方形,图形的长为:3,宽为:2
图形面积为:6
图形属性为:圆形,图形的颜色为:红色,图形的半径为:4
图形面积为:50.24
接口
如果一个抽象类的所有方法都是抽象的,则可以将这个类定义为接口。接口中除了可以包括抽象方法外,还可以包括默认方法和静态方法(也交类方法),默认方法用default修饰,静态方法用static修饰,这两种方法都允许有方法体。
接口通常以interface来声明。一个类通过继承接口的方式,从而来继承接口的抽象方法。
接口并不是类,编写接口的方式和类很相似,但是它们属于不同的概念。类描述对象的属性和方法。接口则包含类要实现的方法。
除非实现接口的类是抽象类,否则该类要定义接口中的所有方法。
接口无法被实例化,但是可以被实现。一个实现接口的类,必须实现接口内所描述的所有方法,否则就必须声明为抽象类。另外,在 Java 中,接口类型可用来声明一个变量,他们可以成为一个空指针,或是被绑定在一个以此接口实现的对象。
- 接口中的方法是不能在接口中实现的,只能由实现接口的类来实现接口中的方法。
接口与类的区别
- 接口不能用于实例化对象。
- 接口没有构造方法。
- 接口中所有的方法必须是抽象方法,Java 8 之后 接口中可以使用 default 关键字修饰的非抽象方法。
- 接口不能包含成员变量,除了 static 和 final 变量。
- 接口不是被类继承了,而是要被类实现。
- 接口支持多继承。
抽象类和接口的区别
- 抽象类中的方法可以有方法体,就是能实现方法的具体功能,但是接口中的方法不行。
- 抽象类中的成员变量可以是各种类型的,而接口中的成员变量只能是 public static final 类型的。
- 接口中不能含有静态代码块以及静态方法(用 static 修饰的方法),而抽象类是可以有静态代码块和静态方法。
- 一个类只能继承一个抽象类,而一个类却可以实现多个接口。
接口的声明
x修饰符 interface 接口名称 extends 接口1,接口2... {
// 声明变量
访问权限 abstract 返回值类型 抽象方法名称 (参数); ///抽象方法
}
- 接口中每一个方法也是隐式抽象的,接口中的方法会被隐式的指定为 public abstract(只能是 public abstract,其他修饰符都会报错)。
- 接口中可以含有变量,但是接口中的变量会被隐式的指定为 public static final 变量(并且只能是 public,用 private 修饰会报编译错误)。
接口的实现
修饰符 class 类名 implements 接口1,接口2...{
...
}
当类实现接口的时候,类要实现接口中所有的方法。否则,类必须声明为抽象的类。
类使用implements关键字实现接口。在类声明中,Implements关键字放在class声明后面。
接口的继承
一个接口能继承另一个接口,和类之间的继承方式比较相似。接口的继承使用extends关键字,子接口继承父接口的方法。
在Java中,类的多继承是不合法,但接口允许多继承。
在接口的多继承中extends关键字只需要使用一次,在其后跟着继承接口,用 ‘ ,’ 分隔。
案例
题目1
设计一个接口circleInterface,要求接口中有一个定义PI的常量以及一个计算圆面积的空方法circleArea()。然后设计一个类circleClass实现该接口,通过构造函数circleClass(double r)定义圆半径,并增加一个显示圆面积的方法。最后,通过上述类生成两个半径分别为3.5、5.0的圆对象circle1、circle2进行测试。
代码
package DEMO230201.Interfacedemo;
interface CircleInterface{
double PI = 3.1415926535; // 定义PI常量
void circleArea();
}
class CircleClass implements CircleInterface{
double r;
public CircleClass(double r){
this.r=r;
}
public void circleArea(){
System.out.println("圆的面积为:"+PI*r*r);
}
}
public class Interface1 {
public static void main(String[] args){
CircleClass circle1=new CircleClass(3.5);
CircleClass circle2=new CircleClass(5.0);
circle1.circleArea();
circle2.circleArea();
}
}
运行结果
圆的面积为:38.484510005375
圆的面积为:78.5398163375
题目2
设计一个Shape接口和它的两个实现类Square和Circle,要求如下:1)Shape接口中有一个抽象方法area(),方法接收一个double类型的参数,返回一个double类型的结果。2)Square和Circle中实现了Shape接口的area()抽象方法,分别求正方形和圆形的面积并返回。在测试类中创建Square和Circle对象,计算边长为2的正方形面积和半径为3的园面积
代码
package DEMO230201.interfacedemo;
interface Shape{
double area(double a);
}
class Square implements Shape{
public double area(double length){
return length*length;
}
}
class Circle implements Shape{
double PI = 3.1415926535;
public double area(double radius){
return PI*radius*radius;
}
}
public class interface2 {
public static void main(String[] args){
Square s=new Square();
Circle c=new Circle();
System.out.println("正方形面积为:"+s.area(2));
System.out.println("圆形面积为:"+c.area(3));
}
}
运行结果
正方形面积为:4.0
圆形面积为:28.2743338815
多态
多态是同一个行为具有多个不同表现形式或形态的能力。
多态存在的三个必要条件
- 继承
- 重写
- 父类引用指向子类对象:Parent p = new Child();
当使用多态方式调用方法时,首先检查父类中是否有该方法,如果没有,则编译错误;如果有,再去调用子类的同名方法。
多态的好处:可以使程序有良好的扩展,并可以对所有类的对象进行通用处理。
向上转型
多态本身就是向上转型过的过程
使用格式:
父类类型 变量名 = new 子类类型();
如:Animal a = new Cat();
- 向上转型后,子类单独定义的方法会丢失(父类并不知道子类定义的新属性与方法)
- 父类引用可以指向子类对象,但是子类引用不能指向父类对象
- 如果子类中重写了父类的方法,那么调用这个方法的时候,将会调用子类中的方法
适用场景:当不需要面对子类类型时,通过提高扩展性,或者使用父类的功能就能完成相应的操作。
假如现在我们写一个方法用来接收不同子类的对象,然后调用它们的print()方法输出各个子类对象的相关信息,那么此时,有多少个子类,我们就需要写多少个方法,分别用来接收各个不同子类的对象
public static void main(String[] args){
Test test=new Test();
test.getMessage(new Student());
test.getMessage(new Teacher());
test.getMessage(new Worker());
}
public void getMessage(Student p) {
p.print();
}
public void getMessage(Teacher p) {
p.print();
}
public void getMessage(Worker p) {
p.print();
}
当我们使用了向上转型之后,就可以只写一个方法来接收所有Person类的子类对象,这样就实现了参数的统一化
public static void main(String[] args){
Test test=new Test();
test.getMessage(new Student());
test.getMessage(new Teacher());
test.getMessage(new Worker());
}
public void getMessage(Person p) {
p.print();
}
向下转型
一个已经向上转型的子类对象可以使用强制类型转换的格式,将父类引用类型转为子类引用各类型
使用格式
子类类型 变量名 = (子类类型) 父类变量名;
如:Cat c =(Cat) a;
适用场景:当要使用子类特有功能时。
向下转型之前一定要进行向上转型!!
否则在转型时会出现ClassCastException(类型转换异常–运行时异常)
想避免这类问题发生,可以先判断(依靠instanceof关键字实现)该引用是否能表示该类实例。
instanceof关键字
JAVA中可以使用instanceof 关键字判断一个对象是否是某个类(或接口)的实例。
对象 instanceof 类 (或接口)
如果对象是指定类的实例对象,则返回true,否则返回false。
eg:
class Person{
public void print(){
System.out.println("I am a person");
}
public void p(){
System.out.println("sad");
}
}
class Student extends Person{
public void print(){
System.out.println("I am a student");
}
public void fun(){
System.out.println("happy");
}
}
public class Test{
public static void main(String[] args)
{
Person per = new Student(); //向上转换
System.out.println(per instanceof Person);
System.out.println(per instanceof Student);
if(per instanceof Student){
Student stu = (Student)per; //向下转换
stu.fun(); //调用特有方法
}
}
}
包
为了更好地组织类,Java 提供了包机制,用于区别类名的命名空间。
包的作用
-
把功能相似或相关的类或接口组织在同一个包中,方便类的查找和使用。
-
如同文件夹一样,包也采用了树形目录的存储方式。同一个包中的类名字是不同的,不同的包中的类的名字是可以相同的,当同时调用两个不同包中相同类名的类时,应该加上包名加以区别。因此,包可以避免名字冲突。
-
包也限定了访问权限,拥有包访问权限的类才能访问某个包中的类。
Java 使用包(package)这种机制是为了防止命名冲突,访问控制,提供搜索和定位类(class)、接口、枚举(enumerations)和注释(annotation)等。
包语句的语法格式为:
package 包名.包名...;
import 关键字
为了能够使用某一个包的成员,我们需要在 Java 程序中明确导入该包。
在 Java 中,import 关键字用于导入其他类或包中定义的类型,以便在当前源文件中使用这些类型。
import 关键字用于引入其他包中的类、接口或静态成员,它允许你在代码中直接使用其他包中的类,而不需要完整地指定类的包名。
在 java 源文件中 import 语句必须位于 Java 源文件的头部,其语法格式为:
import 包名.包名…类名;
如果在一个包中,一个类想要使用本包中的另一个类,那么该包名可以省略。
可以使用 import语句来引入一个特定的类:
import 包名.包名...特定类;
这样,你就可以在当前源文件中直接使用 MyClass 类的方法、变量或常量。
也可以使用通配符 * 来引入整个包或包的子包:
import 包名.包名...*;
这样,你可以导入 com.runoob.mypackage 包中的所有类,从而在当前源文件中使用该包中的任何类的方法、变量或常量。注意,使用通配符 * 导入整个包时,只会导入包中的类,而不会导入包中的子包。
案例
题目1
在biology包中的animal包中有human类,它具有name,height,weight的属性,还具有eat(),sleep()和work()的行为,在biology包中的plant包中有flower类,它具有name,color,smell的属性,还具有drink()和blossom()的行为.现在在一个school包中的garden包中一个张三的人,他是一个human类的对象,种植的rose是一个flower类对象,编程实现并测试各自的方法.
代码
package DEMO230201.packagedemo.package1.biology.animal;
public class human {
String name;
int height;
int weight;
public human(String name,int height,int weight){
this.name=name;
this.height=height;
this.weight=weight;
}
public void eat(){
System.out.println("I am eating");
}
public void sleep(){
System.out.println("I am sleeping");
}
public void work(){
System.out.println("I am working");
}
public void printHuman(){
System.out.println("My name is "+name+",my height is "+height+",my weight is "+weight);
}
public static void main(String[] args){
}
}
package DEMO230201.packagedemo.package1.biology.plante;
public class flower {
String name,color,smell;
public flower(String name,String color,String smell){
this.name=name;
this.color=color;
this.smell=smell;
}
public void drink(){
System.out.println("I am drinking");
}
public void blossom(){
System.out.println("I am blossoming");
}
public void printFlower(){
System.out.println("I am a "+name+",my color is "+color+",my smell is "+smell);
}
public static void main(String[] args){
}
}
package DEMO230201.packagedemo.package1.school;
import DEMO230201.packagedemo.package1.biology.animal.human;
import DEMO230201.packagedemo.package1.biology.plante.flower;
public class garden {
public static void main(String[] args){
human h=new human("张三",188,160);
h.printHuman();
h.eat();
h.sleep();
h.work();
flower f=new flower("芍药","粉白","香");
f.printFlower();
f.drink();
f.blossom();
//测试human类和flower类里面的共五个方法,因为是跨包定义的类,因此这五个方法必须是public权限
}
}
运行结果
My name is 张三,my height is 188,my weight is 160
I am eating
I am sleeping
I am working
I am a 芍药,my color is 粉白,my smell is 香
I am drinking
I am blossoming
题目2
在computer包中的mainbroad包中有一个VGACard的类,它有一个显示方法show(),显示”VGA checked success”,在server的包的mainbroad包中的showCard类是继承自VGACard,请测试showCard的show()功能。
代码
package DEMO230201.packagedemo.package2.computer.mainbroad;
public class VGACard {
public void show(){
System.out.println("VGA checked success");
}
public static void main(String[] args){
}
}
package DEMO230201.packagedemo.package2.server.mainbroad;
import DEMO230201.packagedemo.package2.computer.mainbroad.VGACard;
public class showCard extends VGACard {
public void show(){
super.show();
}
public static void main(String[] args){
showCard s = new showCard();
s.show();
}
}
运行结果
VGA checked success
题目3
在com.graphic包中定义一个圆柱体类Cylinder,其半径r,高h都为私有属性,有构造方法和求体级方法volume()。在com.test包中定义一个测试类test,测试一个半径为5.34、高为2的圆柱体体积。半径PI为3.14
代码
package DEMO230201.packagedemo.pakage3.com.graphic;
public class Cylinder {
private double r;
private double h;
public Cylinder(double r,double h){
this.r=r;
this.h=h;
}
public void volume(){
double PI=3.14;
double area=PI*r*r*h;
System.out.println("圆柱体体积为:"+area);
}
}
package DEMO230201.packagedemo.pakage3.com;
import DEMO230201.packagedemo.pakage3.com.graphic.Cylinder;
public class test {
public static void main(String[] args){
Cylinder c=new Cylinder(5.34,2);
c.volume();
}
}
运行结果
圆柱体体积为:179.077968