2022-07-21 第4组 蒋萍 面向对象(4)
作者:互联网
继承(Java面向对象三大特征之一)
目录-
多个类存在相同属性和行为时,将这此内容抽取单独的一个类中,(类当中抽象出一个类(父类))
-
减少代码量;方便修改代码
-
父类(基类)与子类是is-a关系,子类可以直接访问父类非私有的属性和行为
1.1、使用继承
1.1.1 编写父类A
访问修饰符 class A{
// 公有属性和方法
}
1.1.2 编写子类B,继承父类A
访问修饰符 class B extends A{
// 子类特有的属性和方法
}
- 初始化子类构造方法时,必须先执行父类的构造方法(默认先无参),子类构造方法中默认有一个super(),表示调用的父类的构造方法,先把父类初始化,再把子类初始化。(先有爸爸)
注意
- Java中只支持单根继承,即一个类只能有一个直接父类;
*上图截自菜鸟教程
访问父类的父类的成员,使用一个super. 就可以
1.2 继承的特性
-
子类拥有父类非 private 的属性、方法。
-
子类可以拥有自己的属性和方法,即子类可以对父类进行扩展。
-
子类可以用自己的方式实现父类的方法。
-
提高了类之间的耦合性(继承的缺点,耦合度高就会造成代码之间的联系越紧密,代码独立性越差)。
-
创建子类对象时,父类先被实例化,再实例化子类
-
父类没空参构造器,子类也没有无参构造器
-
当一个类被实例化时,一定先实例化它的父类
-
子类的构造器可以有多个,但必须和父类构造器形式上统一
1.3 子类继承父类的什么?
-
继承public和protected 修饰的属性和方法,不管子类和父类是否在同一包里;
-
继承默认权限修饰符的属性和方法,但子类和父类必须在同一包里;
不能被继承的父类成员:
- private 成员;
- 子类与父类不在同一包下,使用默认访问权限的成员;
- 构造方法(只能调用,不能继承)
1.4 super和this关键字
super:super调父类,指向父类,但不代表任何对象;
this:当前对象
this调当前类,代表当前类的对象(方法调用者),可以作为返回值返回,super不可,super也不能当参数;
如果子类父类中出现了 重名成员变量,这是访问是有影响的!!!
区分同名成员变量,到底是父成员变量还是子成员变量,需要使用super关键字,修饰父类的成员变量,类似于这前所学this;或者可以理解为super可以使被屏蔽掉(子父类成员变量重名了)的成员可见。
使用格式:
super.父类成员变量名
// super();super(name,health,……)——访问父类构造方法
this.子类成员变量名
note:
super调用父类构造方法时只可有一条且必须放在第一句;
super只能出现在子类的普通方法和构造方法里;
super.属性/方法 :调用父类的属性/方法(super可以省略,调构造器不能省略)
1.5 继承条件下构造方法的调用原则
-
不管是用的无参构造方法的还是有参的去构造子类对象,只要子类的相应构造方法 没有通过super显示调用,系统都默认先走父类无参构造,再走相应子类的无参或带参构造;
-
如果子类的相应构造方法里出现 通过super显示调用父类有参构造方法,则执行父类相应有参构造方法,而不执行父类无参构造方法。
note:
调用 自己本类的构造方法:
public PP(){} // 无参构造
public PP(String name,int age){
// 方法体
}
public PP(String name,int age,String sex){
this(name,age); // this写于首行
// …………
}
二、访问权限控制
1、类的访问控制;
2、类成员的访问控制;
常用的访问控制修饰符:
-
public、protected、默认、private
(修饰符用来定义类、方法或者变量,通常放在语句的最前端)
2.1 实现类的访问控制
类的访问修饰符:
-
public修饰符:
公有访问级别,对所有类可见(包括不同包下,导入包即可用)。
使用对象:类、接口、变量、方法
-
默认修饰符(default,就是什么也不写):
包级私有访问级别,在同一包内可见,不使用任何修饰符。
使用对象:类、接口、变量、方法
2.2 类成员的访问修饰符
-
private修饰符:
只有本类可访问。
对象:变量、方法。 注意:不能修饰类(外部类)
-
默认修饰符:(本类、同包)
-
protected修饰符:
对同一包内的类和所有子类可见。(本类、同包、子类)
使用对象:变量、方法。 注意:不能修饰类(外部类)。
-
public修饰符:(任何地方)
三、static 修饰符(静态)
-
成员变量
- 静态变量,可直接通过类名访问
-
成员方法
- 静态方法,可直接通过类名访问
-
代码块
- 静态代码块,当Java虚拟机加载类时,就会执行该代码块
3.1、类的成员变量
3.1.1 类变量(静态变量)
- 被static修饰的变量;注意:局部变量不能被声明为 static 变量。
- 在内存中只有一个拷贝空间;
- 类的内部,可以在任何方法内直接访问静态变量;
- 其它类中可以通过类名直接调用;(创建实例对象后使用也是可以的)
public class Student{
// 实例变量
int age;
String name;
//类变量
public static String email;
}
public class Test{
public static void main(String[] args){
// 使用
System.out.println(Student.email);// 类名.属性
}
}
3.1.2 实例变量
- 没有被static修饰的变量;
- 必须通过实例(对象)调用;
- 每创建一个实例都会分配一次内存,可在内存空间有多个拷贝,且互不影响;
public class Student{
// 实例变量
int age;
String name;
}
public class Test{
public static void main(String[] args){
Student stu=new Student();// 创建实例对象
System.out.println(stu.age);// 使用
}
}
3.1.3 static变量的作用
- 能被类的所有实例共享,可作为实例之间进行交流的共享数据;
- 如果类的所有实例都包含一个相同的常量属性,可把该属性定义为静态常量类型,从而节省内存空间。
// 模拟实现选民投票过程:
// 一群选民进行一次投票,当总票数达到100时停止投票
// 选民类
public class Voter {
private static int count;// 投票总数
private static final int MAX_COUNT=100;// 静态常量
private static String name;
// 构造方法
public Voter(){}
public Voter(String name){
this.name=name;
}
// 还可封装
public void setName(String name){
this.name=name;
}
public String getName(){
return this.name;
}
public void voteFor(){
if (count==MAX_COUNT){
System.out.println("已停止投票!");
return; // 结束程序
}else {
count++;
System.out.println(this.name+"投票成功!");
}
}
}
// 测试类
public class TestVoter {
public static void main(String[] args) {
Voter zhang=new Voter("张三");// 1号选民(构造方法的使用)
zhang.voteFor();
for (int i = 1; i <=99; i++) {
Voter names=new Voter("name"+i);
names.voteFor();
}
}
}
3.2、static 方法
3.2.1 静态方法
-
可直接通过类名访问,(静态方法访问不不能直接访问实例的变量和方法)
-
静态方法中不能使用 this 和 super 为什么呢??
静态方法不需要创建对象,而this和super与对象有关;
-
可直接访问静态变量和静态方法
-
静态方法必须被实现(得有方法体)
为什么main方法(程序入口)是静态 ???
在加载类的时候就会执行静态方法相关代码,而静态方法与类相关,只要加载类,就会执行与类相关的main方法,不用创建类对象就可以执行。
3.2.2 实例方法(普通方法)
- 实例方法可直接访问所属类的静态变量、静态方法、实例变量、实例方法
// 打印投票结果
// 静态方法
public static void printRes(){
System.out.println("投票总数:"+count);
}
// 在测试类里调用
Voter.printRes();// 通过类名调直接用静态方法
3.3、静态代码块
JVM加载类时,加载静态代码块。
-
如果有多个静态代码块,按顺序加载依次执行;
-
每个静态代码块只会被执行一次
public class StaticTest{ static int num=100;// 静态变量 static{ // 两静态代码块依次执行 num+=100; System.out.println(num);// 200 } static{ num+=100; System.out.println(num);// 300 } }
StaticTest st1=new StaticTest(); StaticTest st2=new StaticTest(); System.out.println(StaticTest.num);// 300 // 打印结果为200 200 300
3.4、注意
- 方法里不能定义static 变量但可以在方法里访问静态变量,static 变量只能在类里定义;
在继承只是继承共性而个性没法很好的实现,怎么办呢:
可以先继承共性,再补全个性,于是我们有了方法重写:
四、方法重写(override)
- 如果子类父类出现 重名的成员方法,这是访问一种特殊情况,叫做方法重写(Override)
方法重写时,可以扩展对父类所定义同名方法,是实现多态的基础。
方法重写:父子(返回值类型 方法名 (方法的参数列表))必须都相同
方法重写里可以调用父类的方法;
note:
先得有继承才能有重写(必须是父子类)!!!
- 方法名必须相同;
- 参数列表相同;
- 返回值可以不一样;(返回值类型相同或是其子类)
- 访问权限不严于父类;(要么一样要么比你更宽松,public重写时不能是private)
5、抛出异常:重写方法不能抛出比被重写的方法更大的异常
父类的(非)静态方法不能被子类重写为(静态)非静态;
父类的 私有方法 不能被子类重写;( 此时本就不能继承,更不能重写)
不能抛出比父类更多的异常;
看到这里,我们就更加容易理解到:在子类重写父类方法后,这里我们可以理解为父类的方法被屏蔽(覆盖)了,此时通过super. 就可以被屏蔽的成员变量可见。
// 下面这种写法不常见,这里就是第3、的体现
// 父类方法
public Person m1(){
System.out.println("我是父类的m1方法");
return new Person();
}
// 子类方法
public Student m1(){
// 这里Student是Person的一个子类,所以这也是方法重写
System.out.println("我是子类的m1方法");
return new Student();
}
4.2 方法重载(overload)和方法重写(override)区别
-
类: 方法重载一个类有多个同名方法 ;
父子关系中子类重写父类的方法
-
方法返回值 :方法重载可以修改;
方法重写方法返回值类型必须父类相一致
-
参数列表:方法重载可以修改;
方法重写方法参数列表不能修改
1、重载:同一个类里的多个方法,且它们同名不同参;(与访问权限修饰符、返回值无关)
2、重写:子类里重写父类方法,父子类(两个类),子父类同名同参(注意访问权限和返回值)
4.5、关于super的理解例子
// 找到程序入口然后“顺藤摸瓜”
// 父类
public class Father {
String name="Father";
public void m1(){
System.out.println("我是Father类的m1方法");
}
}
// 子类
public class Son extends Father{
String name="Son";
/*
方法重写了!!!
此时原来父类的m1方法已经被Son类覆盖;
所以在main方法中执行的是本类的方法,
*/
public void m1(){
System.out.println("我是Son类的m1方法");
}
public void varTest(){
super.m1(); // 此时这里才是父类的m1方法
System.out.println(name);// 此时的name="Son"
System.out.println(super.name);// 这里name="Father"
super.m1();// 这时调用的是本类的方法
}
// 程序入口
public static void main(String[] args) {
/* super. 不能出现在静态方法区中 */
Son son=new Son();// 找到Son类
son.varTest();// 方法调用
son.m1();// 执行本类的m1方法
}
}
/*我是Father类的m1方法
Son
Father
我是Father类的m1方法
我是Son类的m1方法
*/
标签:super,07,子类,修饰符,蒋萍,2022,父类,方法,public 来源: https://www.cnblogs.com/fulfill/p/15991849.html