Java抽象类和接口的学习了解
目录
2.抽象类中被 abstract 修饰的方法--抽象方法,该方法不用给出具体的实现体
3.当一个类中含有抽象方法时,该类必须要abstract修饰
1. 抽象类
1.1 抽象类概念
1.2例子
1.3 抽象类语法
1.被 abstract 修饰的类--抽象类
2.抽象类中被 abstract 修饰的方法--抽象方法,该方法不用给出具体的实现体
3.当一个类中含有抽象方法时,该类必须要abstract修饰
4.抽象类也是类,内部可以包含普通方法和属性,甚至构造方法
// 抽象类:被abstract修饰的类
public abstract class Shape {
// 抽象方法:被abstract修饰的方法,没有方法体
abstract public void draw();
abstract void calcArea();
// 抽象类也是类,也可以增加普通方法和属性
public double getArea(){
return area;
}
protected double area; // 面积
}
1.4 抽象类特性
1. 不能直接实例化对象
public abstract class Shape {
abstract public void draw();
abstract void calcArea();
public double getArea(){
return area;
}
protected double area; // 面积
}
public static void main(String[] args) {
Shape shape = new Shape();
}
// 编译出错
Error:(30, 23) java: Shape是抽象的; 无法实例化
2. 抽象方法1
不能是 private 的
abstract class Shape {
abstract private void draw();
}
// 编译出错
Error:(4, 27) java: 非法的修饰符组合: abstract和private
3. 抽象方法2
2不能被final和static修饰,因为抽象方法要被子类重写
public abstract class Shape {
abstract final void methodA();
abstract public static void methodB();
}
// 编译报错:
// Error:(20, 25) java: 非法的修饰符组合: abstract和final
// Error:(21, 33) java: 非法的修饰符组合: abstract和static
4. 抽象类必需被继承
继承后的子类要重写父类的抽象方法,如果子类不重写父类中的抽象方法,那么子类也会成为抽象类,并且必须用 abstract 关键字进行修饰
// 矩形类
public class Rect extends Shape {
private double length;
private double width;
Rect(double length, double width){
this.length = length;
this.width = width;
}
public void draw(){
System.out.println("矩形: length= "+length+" width= " + width);
}
public void calcArea(){
area = length * width;
}
}
// 圆类:
public class Circle extends Shape{
private double r;
final private static double PI = 3.14;
public Circle(double r){
this.r = r;
}
public void draw(){
System.out.println("圆:r = "+r);
}
public void calcArea(){
area = PI * r * r;
}
}
// 三角形类:
public abstract class Triangle extends Shape {
private double a;
private double b;
private double c;
@Override
public void draw() {
System.out.println("三角形:a = "+a + " b = "+b+" c = "+c);
}
// 三角形:直角三角形、等腰三角形等,还可以继续细化
//@Override
//double calcArea(); // 编译失败:要么实现该抽象方法,要么将三角形设计为抽象类
}
5. 抽象类中的包含方法
不一定包含抽象方法,但是有抽象方法的类一定是抽象类
6. 抽象类有构造方法
供子类创建对象时,初始化父类的成员变量
abstract class AbstractBase {
// 抽象类中不一定包含抽象方法,这里没有抽象方法
int someVariable;
public AbstractBase(int someValue) {
// 抽象类中有构造方法,供子类创建对象时初始化父类成员变量
someVariable = someValue;
}
}
class ConcreteSubclass extends AbstractBase {
public ConcreteSubclass(int value) {
super(value);
}
}
1.5 抽象类的作用
2. 接口
2.1 接口的概念
2.2 语法规则
2.2.1定义
2.2.2成员变量
接口当中的成员变量,默认为public static final,一般情况不写
2.2.3成员方法
2.2.4普通方法
接口中不能存在普通方法
2.2.5普通方法使用
硬要在接口中使用普通方法,给普通方法加上default来修饰,java8开始才能使用
2.2.6普通方法使用2
2.2.7接口实例化
2.2.9重写并且引用
快速重写方法(快捷按键组合)
1.鼠标放在标红区域
2.按住alt+回车
蓝色是重写部分选择,黄色是改进这段代码的建议
3.选择蓝色,弹出这个接口下面可以重写的部分,点击选中再点击ok,即可达到重写效果(如果有多个,按住ctrl再点击想要重写的部分,最后点击ok即可)
2.2.10接口向上转型+动态绑定
makeSound
方法的调用在运行时根据对象的实际类型(Dog 或 Cat)动态绑定。
abstract class Animal {
abstract void makeSound();
}
class Dog extends Animal {
void makeSound() {
System.out.println("Woof");
}
}
class Cat extends Animal {
void makeSound() {
System.out.println("Meow");
}
}
public class Test {
public static void main(String[] args) {
Animal myAnimal = new Dog();
myAnimal.makeSound(); // 输出 "Woof"
myAnimal = new Cat();
myAnimal.makeSound(); // 输出 "Meow"
}
}
2.2.11注意
提示:
1. 创建接口时, 接口的命名一般以大写字母 I 开头.
2. 接口的命名一般使用 "形容词" 词性的单词.
2.3接口的使用
接口不能直接使用,必须要有一个"实现类"来"实现"该接口,实现接口中的所有抽象方法。
public class 类名称 implements 接口名称{
// ...
}
注意继承和接口的区别
1.子类和父类之间是extends 继承关系,类与接口之间是 implements 实现关系
2.接口(子implements父)继承(父类extends)
USB例子
// 定义USB接口
public interface USB {
void openDevice();
void closeDevice();
}
// 鼠标类,实现USB
public class Mouse implements USB {
@Override
public void openDevice() {
System.out.println("打开鼠标");
}
@Override
public void closeDevice() {
System.out.println("关闭鼠标");
}
2.4 接口特性
1. 接口类型
是一种引用类型,但是不能直接new接口的对象
public class TestUSB {
public static void main(String[] args) {
USB usb = new USB();
}
}
// Error:(10, 19) java: day20210915.USB是抽象的; 无法实例化
2. 接口方法
都是public的抽象方法, 即接口中的方法会被隐式的指定为 public abstract(只能是 public abstract,其他修饰符都会报错)
public interface USB {
// Error:(4, 18) java: 此处不允许使用修饰符private
private void openDevice();
void closeDevice();
}
3. 接口方法实现
是不能在接口中实现的,只能由实现接口的类来实现
public interface USB {
void openDevice();
// 编译失败:因为接口中的方式默认为抽象方法
// Error:(5, 23) java: 接口抽象方法不能带有主体
void closeDevice(){
System.out.println("关闭USB设备");
}
//改为以下写法即可实现
//方式1
default void closeDevice()
//方式2
public static void closeDevice()
}
4. 重写接口方法
不能使用默认的访问权限
public interface USB {
void openDevice(); // 默认是public的
void closeDevice(); // 默认是public的
}
public class Mouse implements USB {
@Override
void openDevice() {
System.out.println("打开鼠标");
}
}
// 编译报错,重写USB中openDevice方法时,不能使用默认修饰符
// 正在尝试分配更低的访问权限; 以前为public
//修改后
public interface USB {
void openDevice();
void closeDevice();
}
public class Mouse implements USB {
@Override
public void openDevice() { // 显式地使用public修饰符
System.out.println("打开鼠标");
}
@Override
public void closeDevice() { // 同样需要显式地使用public修饰符
System.out.println("关闭鼠标");
}
}
5.接口含有变量
但是接口中的变量会被隐式的指定为 public static final 变量
public interface USB {
double brand = 3.0; // 默认被:final public static修饰
void openDevice();
void closeDevice();
}
public class TestUSB {
public static void main(String[] args) {
System.out.println(USB.brand); // 可以直接通过接口名访问,说明是静态的
// 编译报错:Error:(12, 12) java: 无法为最终变量brand分配值
USB.brand = 2.0; // 说明brand具有final属性
}
}
6. 接口中不能有静态代码块和构造方法
public interface USB {
// 编译失败
public USB(){
}
{} // 编译失败
void openDevice();
void closeDevice();
}
7. 接口虽然不是类
但是接口编译完成后字节码文件的后缀格式也是.class
8. 类没有实现接口所有的抽象方法
则类必须设置为抽象类
9. jdk8中:
接口中还可以包含default方法。
2.5 实现多个接口
在Java中,类和类之间是单继承的,一个类只能有一个父类,即Java中不支持多继承,但是一个类可以实现多个接口。下面通过类来表示一组动物.
class Animal {
protected String name;
public Animal(String name) {
this.name = name;
}
}
另外我们再提供一组接口, 分别表示 "会飞的", "会跑的", "会游泳的".
interface IFlying {
void fly();
}
interface IRunning {
void run();
}
interface ISwimming {
void swim();
}
举例子:猫, 是会跑的.
class Cat extends Animal implements IRunning {
public Cat(String name) {
super(name);
}
@Override
public void run() {
System.out.println(this.name + "正在用四条腿跑");
}
}
鱼, 是会游的.
class Fish extends Animal implements ISwimming {
public Fish(String name) {
super(name);
}
@Override
public void swim() {
System.out.println(this.name + "正在用尾巴游泳");
}
}
小鸟会飞
class Brid extends Animal implements port.IFlying {
public Brid(String name) {
super(name);
}
@Override
public void fly() {
System.out.println(this.name+" 在飞翔着");
}
}
注意:一个类实现多个接口时,每个接口中的抽象方法都要实现,否则类必须设置为抽象类。
快捷实现接口按键ctrl+i
上诉代码优化
猫,鱼,鸟都是会跑的,所有引用时候可以将
cat.run();换成walk(run)
优化优点:有了接口之后, 类的使用者就不必关注具体类型,
2.6 接口间的继承
接口可以继承一个接口, 达到复用的效果. 使用 extends 关键字.
interface IRunning {
void run();
}
interface ISwimming {
void swim();
}
// 两栖的动物, 既能跑, 也能游
interface IAmphibious extends IRunning, ISwimming {
}
class Frog implements IAmphibious {
...
}
2.7 抽象类和接口的区别
核心区别:
抽象类中可以包含普通方法和普通字段, 这样的普通方法和字段可以被子类直接使用(不必重写)
接口中不能包含普通方法, 子类必须重写所有的抽象方法.
如之前写的 Animal 例子. 此处的 Animal 中包含一个 name 这样的属性, 这个属性在任何子类中都是存在的. 因此此处的 Animal 只能作为一个抽象类, 而不应该成为一个接口
class Animal {
protected String name;
public Animal(String name) {
this.name = name;
}
}
3. Object类
Object是Java默认提供的一个类。Java里面除了Object类,所有的类都是存在继承关系的。默认会继承Object父 类。即所有类的对象都可以使用Object的引用进行接收。
范例:使用Object接收所有类的对象
class Person{}
class Student{}
public class Test {
public static void main(String[] args) {
function(new Person());
function(new Student());
}
public static void function(Object obj) {
System.out.println(obj);
}
}
//执行结果:
Person@1b6d3586
Student@4554617c
Object类是参数的最高统一类型。但是Object类也存在有定义好的一些方法。如下:
本小节当中,我们主要来熟悉这几个方法:toString()方法,equals()方法,hashcode()方法
3.1 获取对象信息
如果要打印对象中的内容,可以直接重写Object类中的toString()方法,之前已经讲过了,此处不再累赘。
// Object类中的toString()方法实现:
public String toString() {
return getClass().getName() + "@" + Integer.toHexString(hashCode());
}
3.2 对象比较equals方法
// Object类中的equals方法
public boolean equals(Object obj) {
return (this == obj); // 使用引用中的地址直接来进行比较
}
class Person{
private String name ;
private int age ;
public Person(String name, int age) {
this.age = age ;
this.name = name ;
}
}
public class Test {
public static void main(String[] args) {
Person p1 = new Person("gaobo", 20) ;
Person p2 = new Person("gaobo", 20) ;
int a = 10;
int b = 10;
System.out.println(a == b); // 输出true
System.out.println(p1 == p2); // 输出false
System.out.println(p1.equals(p2)); // 输出false
}
}
Person类重写equals方法后,然后比较:
class Person{
...
@Override
public boolean equals(Object obj) {
if (obj == null) {
return false ;
}
if(this == obj) {
return true ;
}
// 不是Person类对象
if (!(obj instanceof Person)) {
return false ;
}
Person person = (Person) obj ; // 向下转型,比较属性值
return this.name.equals(person.name) && this.age==person.age ;
}
}
结论:比较对象中内容是否相同的时候,一定要重写equals方法。
2.4 hashcode方法
public String toString() {
return getClass().getName() + "@" + Integer.toHexString(hashCode());
}
hashcode方法源码:
public native int hashCode();
我们认为两个名字相同,年龄相同的对象,将存储在同一个位置,如果不重写hashcode()方法,我们可以来看示例 代码:
class Person {
public String name;
public int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
}
public class TestDemo4 {
public static void main(String[] args) {
Person per1 = new Person("gaobo", 20) ;
Person per2 = new Person("gaobo", 20) ;
System.out.println(per1.hashCode());
System.out.println(per2.hashCode());
}
}
//执行结果
460141958
1163157884
注意事项:两个对象的hash值不一样。
class Person {
public String name;
public int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public int hashCode() {
return Objects.hash(name, age);
}
}
public class TestDemo4 {
public static void main(String[] args) {
Person per1 = new Person("gaobo", 20) ;
Person per2 = new Person("gaobo", 20) ;
System.out.println(per1.hashCode());
System.out.println(per2.hashCode());
}
}
//执行结果
460141958
460141958
结论:
1、hashcode方法用来确定对象在内存中存储的位置是否相同
2、事实上hashCode() 在散列表中才有用,在其它情况下没用。在散列表中hashCode() 的作用是获取对象的散列码,进而确定该对象在散列表中的位置。
总结
抽象类:
- 抽象类是一种不能被实例化的类,它通常包含一个或多个抽象方法,这些方法只有声明没有实现,子类必须提供这些方法的具体实现。
- 抽象类可以包含具体方法和变量,这些具体方法可以被子类直接继承和使用。
- 抽象类使用`abstract`关键字声明,它可以有构造方法,但这些构造方法只能被子类调用。
- 抽象类适用于有共同特性和行为的对象,但具体实现需要在子类中完成的情况。
接口:
- 接口是一种完全抽象的类,它定义了一组方法规范,但不提供实现。从Java 8开始,接口可以包含默认方法和静态方法。
- 接口使用`interface`关键字声明,所有方法默认是`public`的,并且从Java 9开始,默认也是`abstract`的。
- 一个类可以实现多个接口,这提供了一种实现多重继承的方式。
- 接口适用于定义操作规范,而具体实现由实现接口的类来完成,这有助于代码的解耦和模块化。
区分
- 抽象类和接口都是定义行为规范的工具,但抽象类更侧重于共享代码和提供默认实现,而接口则侧重于定义操作规范。
- 选择使用抽象类还是接口取决于具体需求。如果需要共享代码,并且类层次结构相对固定,抽象类是更好的选择。如果需要实现多重继承,或者希望代码更加模块化和解耦,接口是更好的选择。
- 在实际开发中,抽象类和接口往往结合使用,以实现复杂的功能和灵活的设计。
通过合理使用抽象类和接口,可以提高代码的可读性、可维护性和可扩展性,同时也能够更好地利用Java的面向对象特性。
希望这篇文章可以帮助到读者更好的了解相关内容。
原文地址:https://blog.csdn.net/2301_80176093/article/details/142265886
免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!