Java面向对象部分重点笔记(一)

Java面向对象部分重点笔记(一)

 

类的定义

在类中,属性是通过成员变量体现的,而行为是成员函数(又称为方法)实现的,下面为大家演示Java中定义类的通用格式,其语法格式如下:

img

 

对象的创建与使用

类是对象的抽象,为对象定义了属性和行为,但类本身既不带任何数据,也不存在于内存空间中。而对象是类的一个具体存在,既拥有独立的内存空间,也存在独特的属性和行为,属性还可以随着自身的行为而发生改变。接下来演示如何用类创建对象,创建对象之前,必须先声明对象,其语法格式如下:

img

类是自定义类型,也是一种引用类型,因此该对象名是一个引用变量,默认值为null,表示不指向任何堆内存空间。接下来需要对该变量进行初始化,Java使用new关键字来创建对象,也称实例化对象,其语法格式如下:

img

声明和实例化对象的过程可以简化,其语法格式如下:

img

演示创建Person类的实例对象,具体示例如下:

img

示例中,“Person p”声明了一个Person类型的引用变量,“new Person()”为对象在堆中分配内存空间,最终返回对象的引用并赋值给变量p,如图所示。

对象实例化后,就可以访问对象的成员变量和成员方法,其语法格式如下:

img

img

img

img

从运行结果可发现,变量p1、p2引用的对象同时调用了say()方法,但输出结果却不相同。这是因为用new创建对象时,会为每个对象开辟独立的堆内存空间,用于保存对象成员变量的值。因此,对变量p1引用的对象属性赋值并不会影响变量p2引用对象属性的值。变量p1、p2引用对象的内存状态如图所示。

img

img

示例中,第16行代码p2被赋值为p1后,会断开原有引用的对象,而和p1引用同一对象。此时,p2原有引用的对象不再被任何变量所引用,就成了垃圾对象,不能再被使用,只等待垃圾回收机制进行回收。垃圾产生的过程如图所示。

 

 

继承

在Java中,子类继承父类的语法格式如下:

img

Java使用extends关键字指明两个类之间的继承关系。子类继承了父类中的属性和方法,也可以添加新的属性和方法。

Java语言只支持单继承,不允许多重继承,即一个子类只能继承一个父类,否则会引起编译错误,具体示例如下:

img

Java语言虽然不支持多重继承,但它支持多层继承,即一个类的父类可以继承另外的父类。因此,Java类可以有无限多个间接父类,具体示例如下:

img

 

重写父类方法

在继承关系中,子类从父类中继承了可访问的方法,但有时从父类继承下来的方法不能完全满足子类需要,例如上例中,如果要求父类与子类中的say()方法输出不同内容,这时就需要在子类的方法里修改父类的方法,即子类重新定义从父类中继承的成员方法,这个过程称为方法重写或覆盖。在进行方法重写时必须考虑权限,即被子类重写的方法不能拥有比父类方法更加严格的访问权限,如例所示。

img

 

重写 总结

重写:需要有继承关系,子类重写父类的方法!

  1. 方法名必须相同

  2. 参数列表必须相同

  3. 修饰符:范围可以扩大 : public > Protected > Default > private

  4. 抛出的异常:范围,可以被缩小,但不能扩大; ClassNotFoundException (范围小) --> Exception (范围大)

重写,子类的方法和父类必须要一致:方法体不同!

为什么需要重写

  1. 父类的功能,子类不一定需要,或者不一定满足!

    插入重写快捷键:Alt + Insert ; 选中 override;

 

 

 

 

 

 

 

 

 

super关键字

当子类重写父类方法后,子类对象将无法访问父类被重写的方法。如果在子类中需要访问父类的被重写方法,可以通过super关键字来实现,其语法格式如下:

img

在继承中,实例化子类对象时,首先会调用父类的构造方法,再调用子类的构造方法,这与实际生活中先有父母再有孩子类似。子类继承父类时,并没有继承父类的构造方法,但子类构造方法可以调用父类的构造方法。在一个构造方法中调用另一个重载的构造方法使用this关键字,在子类构造方法中调用父类的构造方法时应使用super关键字,其语法格式如下:

img

另外,子类中如果没有显式地调用父类的构造方法,将自动调用父类中不带参数的构造方法。

 

super注意点

  1. super调用父类的构造方法,必须在构造方法的第一个

  2. super 必须只能出现在子类的方法或者构造方法中!

  3. super 和 this不能同时调用构造方法!

对比 this

  1. 代表的对象不同:

    this:代表本身调用的当前对象

    super:代表被调用的父类对象的引用

  2. 前提:

    this:没有继承也可以使用

    super:只能在继承条件下才可以使用

  3. 构造方法:

    this() ; 本类的构造

    super() ; 父类的构造

     

 

final关键字

 

final关键字修饰类

在Java中,为了考虑安全因素,要求某些类不允许被继承或不允许被子类修改,这时可以用final关键字修饰。它可用于修饰类、方法和变量,表示“最终”的意思,即用它修饰的类、方法和变量不可改变,具体特点如下:

final修饰的类不能被继承

final修饰的方法不能被子类重写。

final修饰的变量是常量,初始化后不能再修改。

使用final关键字修饰的类称为最终类,表示不能再被其他的类继承,如Java中的String类。接下来演示final修饰类,如例所示。

img

例中,使用final关键字修饰了Parent类。因此,Child类继承Parent类时,程序编译结果报错并提示“无法从最终Parent进行继承”。由此可见,被final修饰的类为最终类,不能再被继承。

final关键字修饰方法

使用final关键字修饰的方法,称为最终方法,表示子类不能重写此方法,接下来演示final修饰方法,如例所示。

img

例中,Parent类中使用final关键字修饰了成员方法say(),Child类继承Parent类并重写了say()方法。程序编译结果报错并提示“被覆盖的方法为final”。由此可见,被final修饰的成员方法为最终方法,不能再被子类重写。

 

final关键字修饰变量

使用final关键字修饰的变量,称为常量,只能被赋值一次。如果再次对该变量进行赋值,则程序在编译时会报错,如例所示。

img

上例中,使用final修饰变量PI,再次对其进行赋值,程序编译结果报错并提示“无法为最终变量PI分配值”。由此可见,final修饰的变量为常量,只能初始化一次,初始化后不能再修改

上例中,使用final修饰的是局部变量,接下来使用final修饰成员变量,如下例所示。

img

例中,在Parent类中使用final修饰了成员变量PI,程序编译结果报错并提示“可能尚未初始化变量PI”。由此可见,Java虚拟机不会为final修饰的变量默认初始化。因此,使用final修饰成员变量时,需要在声明时立即初始化,或者在构造方法中进行初始化。

下面使用构造方法初始化final修饰的成员变量,在Parent类中添加代码具体如下:

此外,final关键字还可以修饰引用变量,表示该变量只能始终引用一个对象,但可以改变对象的内容,有兴趣的读者可以动手验证一下。

 

多态

  • 实现动态编译 ; 提高程序可扩展性

  • 即同一方法可以根据发送对象的不同而采用多种不同的行为方式

  • 一个对象的实际类型时确定的,但可以指向对象的引用类型有很多

 

多态存在的条件:

  • 有继承关系

  • 子类重写父类方法

  • 父类引用指向子类对象

 

 

 

多态部分注意事项

  • 多态是方法的多态,属性没有多态性

  • 需要父类和子类之间有联系,不然容易出现类型转换异常(ClassCastException)!

  • 存在条件:

    • 有继承关系

    • 子类重写父类方法

    • 父类引用指向子类对象

 

不能被重写的方法:

  1. static 方法,属于类,它不属于任何实例

  2. final 常量,被final修饰的方法不能被重写

  3. private方法;私有的,私有的方法不能被重写

 

 

instanceof 和 类型转换

 

System.out.println(X instanceof Y ); 能不能编译通过,取决于X,Y之间是否存在父子关系。 如果x是y的子类型,则会返回true

 

类型转化知识点重点:

  1. 父类引用指向子类的对象

  2. 把子类转换为父类,向上转型

  3. 把父类转换为子类,向下转型,强制转换

  4. 方便方法的调用,减少重复的代码

 

static关键字

//import static java.lang.Math.random;    静态导入包
import static java.lang.Math.random;
public class Application {
   //对象引用时的代码执行运行顺序: 1。静态代码块 2.匿名代码块 3.构造方法
   //静态代码块,再多个对象引用时,只会执行第一次

   //匿名代码块:可以用来给类赋一些初始值
  {
       System.out.println("匿名代码块");
  }
   static {
       System.out.println("静态代码块");
  }
   public Application() {
       System.out.println("构造方法");
  }
   public static void main(String[] args) {
       Application a = new Application();
       System.out.println("=================================");
       Application a2 = new Application();

       System.out.println("=================================");
       System.out.println(random());
  }
}

 

抽象类

 

image-20230227002450542

 

//在class前使用abstract修饰符声明抽象类
//抽象类本质也是类,类只有单继承。   为满足多继承的实际需求,java中接口是多继承的
public abstract class Action {

   //abstract 修饰符加在方法声明中,可以定义抽象方法 ”形成一个约束“
   //abstract ,抽象方法,只有方法名字,没有方法实现
   public abstract void dosomething();

//   抽象类的特点:
//   1. 不能new这个抽象类,只能靠子类去实现它。它只是一种约束~
//   2. 抽象类中可以写普通方法
//   3. 抽象方法必须在抽象类中

 

思考题:

  1. 抽象类可以有构造器吗?

是的,抽象类可以有构造器。因为抽象类是可以被继承的,所以其子类在实例化时需要调用父类的构造器。

抽象类的构造器通常用于初始化父类中的成员变量,并在子类中被继承。抽象类中的构造器不能被实例化,只能通过其子类来调用。

在 Java 中,抽象类可以有多种类型的构造器,例如:无参构造器、带参构造器等。抽象类的构造器在子类中通过 super 关键字调用。

 

  1. 抽象类存在的意义

一: 抽象类的作用 如果你看JDK的源码,就会发现,大部分情况下,你用到的方法,他的父类都是一个抽象类,而不是一个直接的接口。原因主要有两个,接口确实是用来定义规范的,同一个接口下,可能有大量不同的实现,举个例子,List这个接口

boolean add(E e); boolean remove(Object o); boolean addAll(Collection<? extends E> c); ...... 但是你去看ArrayList,第一个继承的是AbstractList(抽象类),然后你去看LinkedList,第一个继承是AbstractSequentialList(抽象类),而AbstractSequentialList也是继承自AbstractList,你肯定知道ArrayList和LinkedList的区别咯。为什么这么做呢,因为有很多方法是可以复用的,比如

public int indexOf(Object o); public int lastIndexOf(Object o); ...... 如果你纯粹用接口的话,你需要写很多重复的代码,一旦修改,你就麻烦了。抽象类的目的在于,你可以更细致化的表明哪些是不同的,哪些是相同的。所以这就是接口和抽象类的简单区别。如果还不理解,你可以看看collection容器类的源码,就会发现了。(抽象类可以默认实现某些方法, 就不需要对一些方法重复实现相同的功能)

二: 抽象类与一般类的区别 抽象类与一般类的区别在于抽象方法。

它的理念是对某个操作下一个定义,但交给子类去实现。

你可能会说,一般类也可以定义一个空方法,然后交给子类实现啊,但这么做因为没有在语法上强迫子类一定要实现这个方法,所以如果子类忘了去实现的话,软件出错的几率会很高。

三: 抽象类与接口的区别 当然,有时候 Java 接口不支持缺省方法,但有了缺省方法的接口,仍然不能代替抽象类,因为没有成员。

接口之所以不能有成员,是因为它只是对象行为的抽象。当你需要定义一个类型,既包含行为抽象,又包含成员来供子类使用时,显然只有抽象类可以满足了。

 

 

 

 

posted @ 2023-02-26 20:54  william996  阅读(67)  评论(0)    收藏  举报