文章目录


因为正在学习中,所以博客(作为笔记)暂时有点乱,每天都在整理(更新)中。

加油。

一、Java面向对象学习的三条主线:(第4-6章)


  1. Java类及类的成员:属性、方法、构造器;代码块、内部类
  2. 面向对象的三大特征:封装性、继承性、多态性、(抽象性)
  3. 其它关键字:this、super、static、final、abstract、interface、package、import等


=============================================================================================正文

主线1

属性:成员变量

方法:构造方法(构造器)、成员方法

代码块:

使用大括号​​{ }​​括起来的代码被称为代码块,根据其位置和声明方式的不同,可以分为:


  • 局部代码块(了解)
  • 构造代码块(重点也是难点)

两个使用场景:
1、提取公因数,把所有构造方法都会执行的语句提取出来
2、如果有一个属性,我希望属于对象,但是我又希望它有初始值
比如新来的员工的工资,自带一套算法,通过工作年限和学历计算工资(写复杂代码给salary赋值)
  • 静态代码块:用​​static​​修饰的构造代码块就是静态代码块
语法:static{}

总结目前给静态成员赋值的方法:
1,显式赋值初始化
2,静态代码块
以上两种是类加载赋值,以下是随着创建对象执行的
3,构造代码块
4,构造器、构造方法

执行顺序如下:
1,2永远在3,4前面,并且1,2的执行顺序和代码顺序有关(类加载永远在创建对象之前)
1,2的顺序是依赖于代码中书写的顺序的,同样的除非你是出面试题,不然不要把静态代码块写在上面
若构造代码块和静态代码块同时给一个静态成员变量赋值,它们在代码中的顺序,并不会对结果造成影响。(静态代码块总是先于构造代码块执行)

除非是静态成员变量需要很复杂的初始化代码,否则没太大必要使用,直接显式赋值就行
  • 同步代码块(多线程讲解)

1、构造方法与构造代码块

编译器会把构造代码块插入到每个构造方法的最前端,构造代码块会在每个构造方法内首先执行。(注意:构造代码块不是在构造函数之前执行,它依托于构造方法的执行)(具体如下)

2、构造代码块的作用

public class ConstructCodeBlock {

{
System.out.println("构造代码块1");
}
public ConstructCodeBlock(){
System.out.println("构造方法");
}
public ConstructCodeBlock(String name){
System.out.println("有参构造方法");
}
{
System.out.println("构造代码块2");
}

public static void main(String[] args) {
new ConstructCodeBlock();
new ConstructCodeBlock("小明");
}
}
输出结果如下:

构造代码块1
构造代码块2
构造方法
构造代码块1
构造代码块2
有参构造方法
这等价于
public class ConstructCodeBlock {

public ConstructCodeBlock(){
System.out.println("构造代码块1");
System.out.println("构造代码块2");
System.out.println("构造方法");
}
public ConstructCodeBlock(String name){
System.out.println("构造代码块1");
System.out.println("构造代码块2");
System.out.println("有参构造方法");
}

public static void main(String[] args) {
new ConstructCodeBlock();
new ConstructCodeBlock("小明");
}
}

一个问题:new过程当中,构造代码块,显式赋值,构造器的执行顺序

1、如果构造器的第一行使用了this调用别的构造器,那么程序会先跳到别的构造器,但是不会执行this构造器代码

2、接下来的顺序为:

先默认初始化所有成员变量

接下来从上到下顺序执行显式赋值和构造代码块

接下来再执行this构造器

最后执行那个​​被调用的构造器​

四种访问权限修饰符

1、类中成员(成员变量、成员方法)的访问权限控制

面向对象学习(规划与总结)_多态

2、类的访问权限控制只有两种

public:对其他任意类可见

default:对同包中的其他类可见

内部类可以有四种访问权限

补充protected:不同包的子类中,只有在子类中创建自身子类对象,才能访问从父类那里继承过来的,protected成员

内部类:(外部类、外围类、内部类)(有点难)

小总结

按照内部类处在类中的位置给内部类分类:

成员内部类:(普通)成员内部类、静态(成员)内部类
局部内部类:(普通的)局部内部类、(特殊的)匿名内部类

OutsideClazz 外部类

EnclosedClazz 外围类
InnerClazz 内部类

​3 + 1 = 4种内部类的代码实现细节​

一、成员内部类(定义在另一个类的成员位置,可以看成该类的一个成员)【两个角度总结】

【角度1 成员内部类的自身特点】==================================
1,访问权限修饰符:有四种访问权限
2,成员特点
成员变量
普通成员变量可以
静态成员(静态变量和静态方法)不可以
静态常量可以,但是不能是static final修饰的非String引用数据类型
static final int D = 30; //不触发类加载
static final String E = "111"; //不触发类加载

成员方法
普通成员方法可以;静态成员方法不可以

构造器
同普通类一样,有构造器,是为了给内部类中的普通成员变量赋值的

【角度2 成员内部类的访问特点】==================================
【1】成员内部类内部访问外围类
1,可以直接访问,不论访问权限
2,如果访问外围类的静态成员,建议加上外围类类名(这样可读性更好)
3,内部类的成员方法中隐含了传参EnclosedClazz.this,表示当前内部类的外围类对象(可以用这个来解决外围类和内部类的同名成员问题)
当然 也隐含了一个this,代表当前内部类对象

【2】外围类访问成员内部类成员
外围类的普通成员方法中 要自己手动创建内部类对象
外围类的静态方法中 必须创建两个对象 外围类对象的基础上创建内部类对象

【3】外部类访问成员内部类成员
必须要先创建外围类对象 再创建内部类对象
注意:外部类访问内部类 受访问权限限制

【4】成员内部类访问外部类成员(了解)
在成员内部类中访问外部类成员,和在普通类中访问其它类成员别无二致
======================================================================================================
二、静态内部类

【角度1 静态内部类自身特点】
1,访问权限修饰符:四种,和成员内部类一样
2,成员特点
成员变量、成员方法、构造方法: 和普通类一模一样 啥都能定义(以上,静态内部类的成员和普通类 别无二致)
3,静态内部类最重要的特点:
本身可以独立做一个类,完全有能力 但是为了保护自己 借了外围类的壳子
成员内部类是要依赖外围类的,但是静态内部类和外围类没有显著的依赖关系
4,类加载的过程对于静态内部类和外围类是完全独立的

【角度2 静态内部类的访问特点】
【1】静态内部类内部访问外围类
必须要创建外围类对象 静态成员推荐类名访问 不受访问权限控制

【2】外围类访问静态内部类成员
创建内部类对象 静态成员推荐类名访问 不受访问权限控制

【3】外部类访问静态内部类成员
可以直接创建 但是它和普通类不同 需要告诉编译器 你这个内部类在哪里
EnclosedClazz.InnerClazz ic = new EnclosedClazz.InnerClazz();
System.out.println(ic.privateVar);

【4】静态内部类访问外部类成员(了解)
在静态内部类中,访问外部类成员,和在普通类中访问其他类成员别无二致

======================================================================================================
三、局部内部类
局部内部类是定义在一个方法或者一个作用域里面的类
它和成员内部类的区别在于局部内部类的访问仅限于方法内或者该作用域内
将局部内部类看成是局部变量即可

【角度1 局部内部类自身特点】
1,访问权限修饰符:和局部变量一样,局部内部类也没有访问权限修饰符
2,成员特点:没有静态的概念,除此之外 和普通类一样
3,定义位置:不要在除了方法之外的地方写
局部内部类的使用条件比较苛刻,轻易不要使用,用也要在方法里用

【角度2 局部内部类的访问特点】
在外围类的其他方法(不是定义局部内部类的方法)能不能访问这个局部内部类?
必然不能访问,出了定义这个局部内部类的方法,就再也找不到它了
所以你想触发它的类加载,必须要在这个方法当中去创建它的对象

1,局部内部类去访问外围类成员 是可以任意访问的,不受访问权限限制
2,外围类中的其他方法去访问局部内部类成员 不能访问的,因为局部内部类只在方法内部生效
3,我们其实是可以在外围类中访问这个内部类的(间接的) 通过调用局部内部类的方法
4,外部类更不能直接访问局部内部类

public void test() { //普通成员方法
//可以直接访问外围类成员,因为普通成员方法隐含this指向当前对象 所以已经有对象
//能够直接访问
class InnerLocalClazz { //局部内部类
//局部内部类的成员方法
public void test() {
System.out.println(a); //0
System.out.println(b); //20
System.out.println(EnclosedClazz.c); //10
}
}
//在方法中创建对象
//触发类加载 也只有这一种方式 只能在这里创建对象
InnerLocalClazz innerLocalClazz = new InnerLocalClazz();
//调用局部内部类的方法
innerLocalClazz.test(); //这里输出3个值
}

======================================================================================================
四、匿名内部类
匿名内部类在实际中的用途
直接作为方法的实际参数
匿名内部类(对象)
public class Demo {
public static void main(String[] args) {
test(new I() {
@Override
public void test() {
System.out.println("1111111");
}
}); //方法要传入一个接口的参数--->实际是要传入一个接口的实现类对象--->创建一个接口的实现类太过于复杂而且我只想用一次--->立刻推出 使用匿名内部类

test2(new B(){
@Override
void test() {
System.out.println("22222222");
}
});
//方法要传入一个抽象类--->实际要传入抽象类的具体子类对象----->创建一个抽象类的具体实现子类过于复杂,我只想要一次
//--->立刻推出 使用匿名内部类

test3(new C(){
@Override
public void test() {
System.out.println("C Son");
}
});
//除了可以传入C的对象 也可以传入C的子类.可以用匿名内部类


}

public static void test(I i) {
//System.out.println(i);
i.test();
}

public static void test2(B b) {
System.out.println(b);
//b.test();
}

public static void test3(C c){
c.test();
}
}

interface I {
void test();
}

abstract class B {
abstract void test();
}
class C{
public void test(){
System.out.println("C");
}
}

==============================================================================================
匿名内部类对象做为方法的返回值

public class Demo {
public static void main(String[] args) {
test(new I() {
@Override
public void test() {
System.out.println("1111111");
}
}); //方法要传入一个接口的参数--->实际是要传入一个接口的实现类对象--->创建一个接口的实现类太过于复杂而且我只想用一次--->立刻推出 使用匿名内部类

test2(new B(){
@Override
void test() {
System.out.println("22222222");
}
});
//方法要传入一个抽象类--->实际要传入抽象类的具体子类对象----->创建一个抽象类的具体实现子类过于复杂,我只想要一次
//--->立刻推出 使用匿名内部类

test3(new C(){
@Override
public void test() {
System.out.println("C Son");
}
});
//除了可以传入C的对象 也可以传入C的子类.可以用匿名内部类


}

public static void test(I i) {
//System.out.println(i);
i.test();
}

public static void test2(B b) {
System.out.println(b);
//b.test();
}

public static void test3(C c){
c.test();
}
}

interface I {
void test();
}

abstract class B {
abstract void test();
}
class C{
public void test(){
System.out.println("C");
}
}
外部类
public class OutsideClazz {
public static void main(String[] args) {

}
}


外围类
class EnclosedClazz{

(1)成员方法
public void test(){
访问内部类成员必须要有内部类对象,要自己创建对象
这里已经存在了一个外围类对象,用this指示 所以直接创建内部类对象即可
InnerClazz innerClazz = new InnerClazz();
System.out.println(innerClazz.a);//不受访问权限限制
}
(2)静态方法
public static void testStatic(){
创建外围类对象和创建内部类对象 二合一
InnerClazz innerClazz1 = new EnclosedClazz().new InnerClazz();
}
成员内部类
class InnerClazz{
static final int D = 300;
private int a = 10;
//依赖方法访问外围类成员 成员内部类的成员方法必然是成员内部类的对象去调用的【理解】
public void testInner(){
System.out.println(EnclosedClazz.this.a);//访问外围类的同名普通成员变量
System.out.println(b);//成员内部类是外围类的一个成员,不存在访问权限
System.out.println(EnclosedClazz.c);//访问外围类的静态成员,建议加上外围类类名(这样可读性更好)
System.out.println(EnclosedClazz.D);//同上 这里D为外围类中的30
System.out.println(this.D);//也隐含了一个this,代表当前内部类对象 这里D为300

}
}

}

概念和分类

在Java中,允许一个类的定义位于另一个类的内部,前者称为内部类,后者称为外部类。

​成员内部类​​(static成员内部类和非static成员内部类)

​局部内部类​​(不谈修饰符)、匿名内部类

使用

​1、成员内部类​

一、成员内部类作为类的成员的角色:

1、和外部类不同,Inner class还可以声明为private或protected(4种访问权限修饰符)
2、可以调用外部类的结构
3、Inner class 可以声明为static的,但此时就不能再使用外层类的非static的成员变量;

二、成员内部类作为类的角色:

1、可以在内部定义属性、方法、构造器等结构
2、可以声明为abstract类 ,因此可以被其它的内部类继承
3、可以声明为final的
4、编译以后生成OuterClass$InnerClass.class字节码文件(也适用于局部内部类)

【注意】

1、外部类访问成员内部类的成员,需要“内部类.成员”或“内部类对象.成员”的方式
2、成员内部类可以直接使用外部类的所有成员,包括私有的数据
3、当想要在外部类的静态成员部分使用内部类时,可以考虑内部类声明为静态的

具体的访问特点:(外部类、外围类、内部类)

成员内部类内部访问外围类

1,可以直接访问,不论访问权限
2,如果访问外围类的静态成员,建议加上外围类类名(这样可读性更好)
3,如果内部类中有和外围类同名的普通变量咋办?
普通类的成员方法中隐含了this传参 代表当前对象
而成员内部类的成员方法中也隐含了一个参数 代表当前内部类的外围类对象。用EnclosedClazz.this标记它

外围类访问成员内部类成员

在外围类中如果要访问成员内部类的成员,必须  【先创建一个成员内部类的对象】  ,再通过这个内部类的引用去访问内部类的成员
在外围类中创建内部类语法:

EnclosedClazz oz = new EnclosedClazz();
InnerClazz ic = oz.new InnerClazz();//该形式适合在外围类中创建内部类
或者
InnerClazz ic2 = new EnclosedClazz().new InnerClazz();//该形式适合在外围类中创建内部类

外部类访问成员内部类成员

成员内部类访问外部类成员(了解)

​2、静态内部类​

静态内部类创建对象不依赖于外围类

静态内部类的类加载机制:和外围类相互独立

​3、局部内部类​

看成局部变量就可以

作用域仅限于方法内部


主线2

封装性:使用get( )和set( )方法来获取和设置成员变量(已私有化)的值

Java提供了4种权限修饰符来修饰类及类的内部结构,体现类及类的内部结构在被调用时的可见性的大小。

封装性的一个完整体现

将类的属性xxx私有化(private)
提供公共的(public)方法来获取(getXxx)和设置(setXxx)此属性的值
class Animal{

String name;
private int age; //同legs
private int legs;//腿的个数

//对属性的设置
public void setLegs(int l){
if(l >= 0 && l % 2 == 0){
legs = l;
}else{
legs = 0;
// 抛出一个异常(暂时没有讲)
}
}

//对属性的获取
public int getLegs(){
return legs;
}
}
应该认真考虑成员变量的访问权限,普遍来说,成员变量应该设置成private
为了修改成员变量的值,提供public的set方法;为了访问成员变量的值,提供public的get方法

继承性(hierarchy):[访问权限修饰符] class 类名 extends 被继承的类{}

第一点:引入和语法

以往我们会在类中,使用方法将重复的代码进行复用,那么现在呢? 我们如果去复用类中定义的成员呢?
这种复用类中成员的机制,就是Java的继承机制

语法: [访问权限修饰符] class 类名 extends 被继承的类{}
也即 class Subclass extends SuperClass{ }

继承的本质:和方法一样,都是复用代码
当多个类中存在相同属性和行为时,将这些内容抽取到单独一个类中
多个类无需再定义这些属性和行为,只要继承那个类即可

继承是整个Java的核心技术之一,没有继承就没有Java,也就没有java
第二点:子类和父类

被继承的类,我们称之为:超类(superclass)、基类(base class)、父类(parent class)
继承其他类的类,我们称之为:子类(subclass)、派生类(derived class)、孩子类(child class)

子类和父类表现为“is-a”的关系,仔细来说是“子类is-a父类”,也就是说子类可以看成是一个父类。反之则不行
代码层次,父类的引用指向一个子类的对象,语法上正确
逻辑层次,
子类是父类的扩展,子类一定是一个父类
子类继承了父类的成员,实际上完全可以作为一个父类使用

尽管父类也被称作超类,但实际上子类的功能大多要比父类多
子类可以在继承父类成员的基础上扩展父类
第三点:继承优缺点

优点:代码复用了;代码的可维护性增强了,扩展性增加了
缺点:
第四点:注意事项
1、Java只支持单继承和多层继承,不允许多重继承

2、父类的private成员、父类的构造方法
不能继承父类的私有成员
不能继承父类的构造方法,但是使用super关键字可以调用。

3、适合使用继承的情况:
考虑代码复用只是最粗显的,不是最正确的。我们使用继承,最应该考虑两个类之间的关系。看一看两个类之间有没有is-a的关系

方法的重写(override/overwrite)

在子类中可以根据需要对  从父类中继承来的方法进行改造,也称为方法的重置、覆盖。

1、子类重写的方法必须和父类被重写的方法具有相同的方法名称、参数列表

2、子类重写的方法的返回值类型不能大于父类被重写的方法的返回值类型

3、子类重写的方法使用的访问权限不能小于父类被重写的方法的访问权限

4、子类方法抛出的异常不能大于父类被重写方法的异常

子类与父类中同名同参数的方法必须同时声明为非static的(即为重写),或者同时声明为static的(不是重写)。因为static方法是属于类的,子类无法覆盖父类的方法。

public class Person {
public String name;
public int age;
public String getInfo() {
return "Name: "+ name + "\n" +"age: "+ age;
}
}

public class Student extends Person {
public String school;

public String getInfo() { //重写方法============================================
return "Name: "+ name + "\nage: "+ age + "\nschool: "+ school;
}
public static void main(String args[]){
Student s1=new Student();
s1.name="Bob";
s1.age=20;
s1.school="school2";
System.out.println(s1.getInfo()); //Name:Bob age:20 school:school2
}
}
Person p1=new Person();
//调用Person类的getInfo()方法
p1.getInfo();

Student s1=new Student();
//调用Student类的getInfo()方法
s1.getInfo();


这是一种“多态性”:同名的方法,用不同的对象来区分调用的是哪一个方法。

重写(override/overwrite)和重载(overload)的对比

重载(overload)

重写(override)

发生的类不同

发生在同类中

发生在子父类之间,肯定不是一个类

方法名

必须相同

必须相同

参数列表

必须不同

必须相同

权限修饰符

不影响

重写的方法访问权限必须大于等于原方法

异常

不影响

重写的方法不能抛出更多的异常

返回值类型

不影响

重写的方法的返回值类型必须和原方法兼容,代表可以不是完全一致

对于重载而言,在方法调用之前,编译器就已经确定了所要调用的方法,这称为“早绑定”或“静态绑定”; 

而对于多态,只有等到方法调用的那一刻,解释运行器才会确定所要调用的具体方法,这称为“晚绑定”或“动态绑定”。

子类对象的初始化问题(子类对象的实例化过程)(第8天学习重点)(JVM运行时内存图)

​理解,会画​

面向对象学习(规划与总结)_java_02

多态性(polymorphic)(抽象性):父类的引用指向不同的子类的对象,在行为上表现不同(对象的多态性)

可以直接应用在抽象类和接口上

多态总结(只看这个就够了)(多态性的核心内容就两个:即下面的何为多态性和多态的使用):
1、何谓多态性?
对象的多态性:父类的引用指向子类的对象(或子类的对象赋给父类的引用)
2、多态的使用?
编译期:只能调用父类中声明的方法(编译时看左边)
运行期:实际执行的是子类重写父类的方法(运行时看右边)
3、多态性的使用前提?
(1)类的继承关系
(2)方法的重写
4、对象的多态性:只适用于方法,不适用于属性(编译和运行都看左边)。

虚拟方法调用的理解:
(1)正常的方法调用
Person e = new Person();
e.getInfo();
Student e = new Student();
e.getInfo();
(2)虚拟方法调用(多态情况下)
子类中定义了与父类同名同参数的方法,在多态情况下,将此时父类的方法称为虚拟方法,
父类根据赋给它的不同子类对象,动态调用属于子类的该方法。
这样的方法调用在编译期是无法确定的。

Person e = new Student();
e.getInfo(); //调用Student类的getInfo()方法
编译时e为Person类型,而方法的调用是在运行时确定的,所以调用的是Student类 的getInfo()方法。——动态绑定
==========================================================================================================
前提:(1)需要存在继承或者实现关系;(2)有方法的重写
成员方法:
 编译时:要查看引用变量所声明的类中是否有所调用的方法。
 运行时:调用实际new的对象所属的类中的重写方法。
成员变量:不具备多态性,只看引用变量所声明的类。
多态作用:提高了代码的通用性,常称作接口重用
面试题:多态是编译时行为还是运行时行为?     运行时行为

​多态是运行时行为,见如下面试题。​

第一点:
Java引用变量有两个类型:编译时类型和运行时类型。编译时,看左边;运行时,看右边。

若编译时类型和运行时类型不一致,就出现了对象的多态性(Polymorphism)
多态情况下,“看左边”:看的是父类的引用(父类中不具备子类特有的方法)
“看右边”:看的是子类的对象(实际运行的是子类重写父类的方法)

第二点:
Person p = new Student();
Object o = new Person();//Object类型的变量o,指向Person类型的对象
o = new Student(); //Object类型的变量o,指向Student类型的对象
子类可看做是特殊的父类,所以父类类型的引用可以指向子类的对象:向上转型(upcasting)。

第三点:
Student m = new Student();
m.school = “pku”; //合法,Student类有school成员变量
Person e = new Student();
e.school = “pku”; //非法,Person类没有school成员变量
一个引用类型变量如果声明为父类的类型,但实际引用的是子类对象,那么该变量就不能再访问子类中添加的属性和方法
属性是在编译时确定的,编译时e为Person类型,没有school成员变量,因而编译错误。

第四点:虚拟方法调用(Virtual Method Invocation)
Person e = new Student();
e.getInfo(); //调用Student类的getInfo()方法
子类中定义了与父类同名同参数的方法,在多态情况下,将此时父类的方法称为虚拟方法,父类根据赋给它的不同子类对象,动态调用属于子类的该方法。这样的方法调用在编译期是无法确定的。
编译时e为Person类型,而方法的调用是在运行时确定的,所以调用的是Student类 的getInfo()方法。——————动态绑定

举例如下:Person类中定义了welcome()方法,各个子类重写了welcome()。
执行:多态的情况下,调用对象的welcome()方法,实际执行的是子类重写的方法。

instanceof 操作符

x instanceof A:检验x是否为类A的对象,返回值为boolean型。

要求x所属的类与类A必须是子类和父类的关系,否则编译错误。

如果x属于类A的子类B,x instanceof A值也为true。

对象类型转换 (Casting )

面向对象学习(规划与总结)_父类_03

Person p1 = new Person();

Person p2 = new Man();
Person p3 = new Woman();
不能调用子类所特有的方法、属性:编译时,p2是Person类型。
p2.name = "Tom"; //ok
p2.earnMoney(); //error
p2.isSmoking = true; //error
有了对象的多态性以后,内存中实际上是加载了子类特有的属性和方法的,但是由于变量声明为父类类型,导致编译时,只能调用父类中
声明的属性和方法。子类特有的属性和方法不能调用。

如何才能调用子类特有的属性和方法? 向下转型:使用强制类型转换符。
Man m1 = (Man)p2;
m1.earnMoney(); //ok
m1.isSmoking = true; //ok

使用强转时,可能出现ClassCastException的异常。
Woman w1 = (Woman)p2;
w1.goShopping();

为了避免在向下转型时出现ClassCastException的异常,我们在向下转型之前,先进行instanceof的判断,一旦返回true,
就进行向下转型。如果返回false,不进行向下转型

如果 a instanceof A返回true,则 a instanceof B也返回true. 其中,类B是类A的父类
if(p2 instanceof Woman){
Woman w1 = (Woman)p2;
w1.goShopping();
System.out.println("******Woman******"); //不执行
}

if(p2 instanceof Man){
Man m2 = (Man)p2;
m2.earnMoney();
System.out.println("******Man******"); //执行
}

if(p2 instanceof Person){
System.out.println("******Person******"); //执行
}
if(p2 instanceof Object){
System.out.println("******Object******"); //执行
}

运行结果:

男人负责挣钱养家
******Man******
******Person******
******Object******

主线3

this:指向当前对象

super:在Java类中使用super来调用父类中的指定操作(属性、方法、构造器)

面向对象学习(规划与总结)_面向对象编程_04

面向对象学习(规划与总结)_面向对象编程_05

1.super可用于访问父类中定义的属性
2.super可用于调用父类中定义的成员方法
3.super可用于在子类构造器中调用父类的构造器

比如:
return super.getInfo() + "\nschool: " + school;

1.尤其当子父类出现同名成员时,可以用super表明调用的是父类中的成员

2.super的追溯不仅限于直接父类

3.super和this的用法相像,this代表本类对象的引用,super代表父类的内存空间的标识

class Person {
protected String name = "张三"; 关键字super举例
protected int age;
public String getInfo() {
return "Name: " + name + "\nage: " + age;
}
}

class Student extends Person {
protected String name = "李四";
private String school = "New Oriental";
public String getSchool() {
return school;
}
public String getInfo() {
return super.getInfo() + "\nschool: " + school; // super.getInfo() 调用父类中定义的成员方法
}
}

public class StudentTest {
public static void main(String[] args) {
Student st = new Student();
System.out.println(st.getInfo());
}
}
调用父类的构造器:

1、子类中所有的构造器默认都会访问父类中空参数的构造器。(JVM保证)
2、当父类中没有空参数的构造器时,子类的构造器必须通过this(参数列表)或者super(参数列表)语句指定调用本类或者父类中相应的构造器。
同时,只能”二选一”,且必须放在构造器的首行。(程序员代码保证)

3、如果子类构造器中既未显式调用父类或本类的构造器,且父类中又没有无参的构造器,则编译出错。 如下举例:

​super(参数列表)​

public class Student extends Person {
private String school;
public Student(String name, int age, String s) {
super(name, age);
school = s;
}
public Student(String name, String s) {
super(name);
school = s;
}
// 编译出错: no super(),系统将调用父类无参数的构造器。 但是父类中没有无参构造器,编译出错
public Student(String s) {
school = s;
}
}

this和super的区别

面向对象学习(规划与总结)_父类_06

static:静态

final:声明类、变量和方法时,使用关键字final来修饰,表示“最终的”。

1、final标记的类不能被继承。提高安全性,提高程序的可读性。

String类、System类、StringBuffer类
Math数学工具类
所有基本数据类型的包装类和一个Void(Boolean,Character,Short,Integer,Long,Float,Double,Byte,Void)

2、final标记的方法不能被子类重写。

比如:Object类中的getClass()

3、final标记的变量(成员变量或局部变量)即称为常量。名称大写,且只能被赋值一次。

final double MY_PI = 3.14;
final标记的成员变量必须在声明时或在每个构造器中或代码块中显式赋值,然后才能使用。
static final:全局常量
修饰静态成员变量,表示全局常量

final修饰引用数据类型:引用指向的对象不可变,但对象的状态可变
public final class Test {
public static int totalNumber = 5;
public final int ID;
public Test() {
ID = ++totalNumber; // 可在构造器中给final修饰的“变量”赋值
}
public static void main(String[] args) {
Test t = new Test();
System.out.println(t.ID);
final int I = 10;
final int J;
J = 20; // 可以在声明时显式赋值
J = 30; // 非法
}
}

abstract:用abstract关键字来修饰一个类或方法,称为抽象类或抽象方法。(超类声明一个方法但不提供实现,该方法的实现由子类提供)

1、用abstract关键字来修饰一个类,这个类叫做抽象类

2、用abstract来修饰一个方法,该方法叫做抽象方法

抽象方法:只有方法的声明,没有方法的实现。以分号结束:
比如:public abstract void talk();

3、含有抽象方法的类必须被声明为抽象类

4、抽象类不能被实例化。抽象类是用来被继承的,抽象类的子类必须重写父类的抽象方法,并提供方法体。

若没有重写全部的抽象方法,仍为抽象类。

5、不能用abstract修饰变量、代码块、构造器;不能用abstract修饰私有方法、静态方法、final的方法、final的类。

抽象类举例
//抽象类
abstract class A {
abstract void m1();
public void m2() {
System.out.println("A类中定义的m2方法");
}
}
//子类
class B extends A {
void m1() {
System.out.println("B类中定义的m1方法");
}
}
//测试类
public class Test1 {
public static void main(String args[]) {
A a = new B();
a.m1(); //B类中定义的m1方法
a.m2(); //A类中定义的m2方法
}
}

interface接口 ​​[访问权限修饰符] interface 接口名{}​

​class SubClass extends SuperClass implements InterfaceA{ }​

1、接口(interface)是抽象方法和常量值定义的集合


接口不是类,而是一种独立的数据类型,和class并列
类实现接口本质上也是一种继承,接口的实现类是接口的子类


接口的特点:

1、用interface来定义。
2、接口中的所有成员变量都默认是由public static final修饰的
3、接口中的所有抽象方法都默认是由public abstract修饰的
4、接口中没有构造器
5、接口采用多继承机制

面向对象学习(规划与总结)_多态_07

2、一个类继承接口,称之为实现接口,使用关键字implements

​class SubClass extends SuperClass implements InterfaceA{ }​

实现接口的类中必须【提供接口中所有方法的具体实现内容】,方可实例化。否则,仍为抽象类。
接口的主要用途就是【被实现类实现】(面向接口编程)

一个类可以实现多个接口,接口也可以继承其它接口。

接口和类是并列关系,或者可以理解为一种特殊的类。从本质上讲,接口是一种特殊的抽象类,这种抽象类中只包含常量和方法的定义
(JDK7.0及之前),而没有变量和方法的实现。
抽象类:教练类
abstract class AbstractCoach{
String name;
int age;
double salary;
String teach; //教授的东西

public abstract void teach(); //抽象方法

//写教练类的有参构造方法、无参构造方法
public AbstractCoach(String name, int age, double salary, String teach) {
this.name = name;
this.age = age;
this.salary = salary;
this.teach = teach;
}
public AbstractCoach() {
}
}

接口:为了出国交流,跟乒乓球相关的人员都需要学习英语
interface Learning{
void learningEnglish();
}
//==================================================================================================
具体类
//乒乓球教练类
class TableTennisCoach extends AbstractCoach implements Learning{
@Override
public void teach() {
System.out.println("我教乒乓球运动");
}
public TableTennisCoach() {
}
//乒乓球教练类 的 构造器
public TableTennisCoach(String name, int age, double salary, String teach) {
super(name, age, salary, teach);
}

@Override
public void learningEnglish() {
System.out.println("乒乓球教练学习英语,方便出国。");
}
}

面向对象学习(规划与总结)_多态_08

package:声明该Java源文件中,所定义的所有类,都属于同一个包

import:使用import关键字,声明要使用的类

备忘

抽象类使用较少,接口天天用。