Java面向对象部分重点笔记(一)
类的定义

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

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

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

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

示例中,“Person p”声明了一个Person类型的引用变量,“new Person()”为对象在堆中分配内存空间,最终返回对象的引用并赋值给变量p,如图所示。
对象实例化后,就可以访问对象的成员变量和成员方法,其语法格式如下:




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


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

Java使用extends关键字指明两个类之间的继承关系。子类继承了父类中的属性和方法,也可以添加新的属性和方法。
Java语言只支持单继承,不允许多重继承,即一个子类只能继承一个父类,否则会引起编译错误,具体示例如下:

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

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

重写 总结
重写:需要有继承关系,子类重写父类的方法!
-
方法名必须相同
-
参数列表必须相同
-
修饰符:范围可以扩大 : public > Protected > Default > private
-
抛出的异常:范围,可以被缩小,但不能扩大; ClassNotFoundException (范围小) --> Exception (范围大)
重写,子类的方法和父类必须要一致:方法体不同!
为什么需要重写
-
父类的功能,子类不一定需要,或者不一定满足!
插入重写快捷键:Alt + Insert ; 选中 override;
super关键字
当子类重写父类方法后,子类对象将无法访问父类被重写的方法。如果在子类中需要访问父类的被重写方法,可以通过super关键字来实现,其语法格式如下:

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

另外,子类中如果没有显式地调用父类的构造方法,将自动调用父类中不带参数的构造方法。
super注意点
-
super调用父类的构造方法,必须在构造方法的第一个
-
super 必须只能出现在子类的方法或者构造方法中!
-
super 和 this不能同时调用构造方法!
对比 this:
-
代表的对象不同:
this:代表本身调用的当前对象
super:代表被调用的父类对象的引用
-
前提:
this:没有继承也可以使用
super:只能在继承条件下才可以使用
-
构造方法:
this() ; 本类的构造
super() ; 父类的构造
final关键字
final关键字修饰类
在Java中,为了考虑安全因素,要求某些类不允许被继承或不允许被子类修改,这时可以用final关键字修饰。它可用于修饰类、方法和变量,表示“最终”的意思,即用它修饰的类、方法和变量不可改变,具体特点如下:
final修饰的类不能被继承
final修饰的方法不能被子类重写。
final修饰的变量是常量,初始化后不能再修改。
使用final关键字修饰的类称为最终类,表示不能再被其他的类继承,如Java中的String类。接下来演示final修饰类,如例所示。

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

例中,Parent类中使用final关键字修饰了成员方法say(),Child类继承Parent类并重写了say()方法。程序编译结果报错并提示“被覆盖的方法为final”。由此可见,被final修饰的成员方法为最终方法,不能再被子类重写。
final关键字修饰变量
使用final关键字修饰的变量,称为常量,只能被赋值一次。如果再次对该变量进行赋值,则程序在编译时会报错,如例所示。

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

例中,在Parent类中使用final修饰了成员变量PI,程序编译结果报错并提示“可能尚未初始化变量PI”。由此可见,Java虚拟机不会为final修饰的变量默认初始化。因此,使用final修饰成员变量时,需要在声明时立即初始化,或者在构造方法中进行初始化。
下面使用构造方法初始化final修饰的成员变量,在Parent类中添加代码具体如下:
此外,final关键字还可以修饰引用变量,表示该变量只能始终引用一个对象,但可以改变对象的内容,有兴趣的读者可以动手验证一下。
多态
-
实现动态编译 ; 提高程序可扩展性
-
即同一方法可以根据发送对象的不同而采用多种不同的行为方式
-
一个对象的实际类型时确定的,但可以指向对象的引用类型有很多
多态存在的条件:
-
有继承关系
-
子类重写父类方法
-
父类引用指向子类对象
多态部分注意事项
-
多态是方法的多态,属性没有多态性
-
需要父类和子类之间有联系,不然容易出现类型转换异常(ClassCastException)!
-
存在条件:
-
有继承关系
-
子类重写父类方法
-
父类引用指向子类对象
-
不能被重写的方法:
-
static 方法,属于类,它不属于任何实例
-
final 常量,被final修饰的方法不能被重写
-
private方法;私有的,私有的方法不能被重写
instanceof 和 类型转换
System.out.println(X instanceof Y ); 能不能编译通过,取决于X,Y之间是否存在父子关系。 如果x是y的子类型,则会返回true
类型转化知识点重点:
-
父类引用指向子类的对象
-
把子类转换为父类,向上转型
-
把父类转换为子类,向下转型,强制转换
-
方便方法的调用,减少重复的代码
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());
}
}
抽象类
//在class前使用abstract修饰符声明抽象类
//抽象类本质也是类,类只有单继承。 为满足多继承的实际需求,java中接口是多继承的
public abstract class Action {
//abstract 修饰符加在方法声明中,可以定义抽象方法 ”形成一个约束“
//abstract ,抽象方法,只有方法名字,没有方法实现
public abstract void dosomething();
// 抽象类的特点:
// 1. 不能new这个抽象类,只能靠子类去实现它。它只是一种约束~
// 2. 抽象类中可以写普通方法
// 3. 抽象方法必须在抽象类中
思考题:
-
抽象类可以有构造器吗?
是的,抽象类可以有构造器。因为抽象类是可以被继承的,所以其子类在实例化时需要调用父类的构造器。
抽象类的构造器通常用于初始化父类中的成员变量,并在子类中被继承。抽象类中的构造器不能被实例化,只能通过其子类来调用。
在 Java 中,抽象类可以有多种类型的构造器,例如:无参构造器、带参构造器等。抽象类的构造器在子类中通过 super 关键字调用。
-
抽象类存在的意义
一: 抽象类的作用 如果你看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 接口不支持缺省方法,但有了缺省方法的接口,仍然不能代替抽象类,因为没有成员。
接口之所以不能有成员,是因为它只是对象行为的抽象。当你需要定义一个类型,既包含行为抽象,又包含成员来供子类使用时,显然只有抽象类可以满足了。
浙公网安备 33010602011771号