(超详细笔记整理)动力节点_老杜 | JavaSE零基础 :P329(方法) - P479

11 篇文章 5 订阅
3 篇文章 17 订阅

JAVA基础学习

第二篇文章的连接: (超详细笔记整理)动力节点_老杜 | JavaSE进阶 【P486之后】.

文章目录

方法

Java的主要内存空间

image-20210628210102477

代码放在方法区侯先去执行main文件时,main方法在执行的时候 ,方法需要的内存空间在栈中被分配,栈是一种数据结构

栈数据结构

栈数据结构:stack

什么是数据结构?

​ 数据结构通常是:存储数据的容器,而该容器可能存在不同的结构。数据结构和java语言实际上没有任何关系,数据结构是一门独立的学科

常见的数据结构是哪些?

​ 数组、链表、图二叉、队列…java语言已经把数据结构写好了,程序员直接来用就可以。

和数据结构通常在一起的是算法:排序、查找等…

精通数据结构和算法,会提升程序的效率

image-20210628204335607

方法区执行时内存的变换

入栈

image-20210628211051083

弹栈

image-20210628211331721

3个ix都是局部变量,方法结束后,内存空间都进行释放

package Method;
//局部变量只在方法体中有效,方法结束后,局部变量内存就失效了
//方法区中最先有数据,方法区中放代码片段,存放class字节码
//栈内存:方法调用的时候,该方法需要的内存空间在栈中分配
//方法不调用是不会在栈中分配空间的,方法只有调用的时候才会在栈中分配空间,并且调用时就是压栈
//方法执行结束后,该方法所需要的空间就会被释放,进行弹栈。
    /*
    方法调用叫做:压栈,分配空间
    方法结束:弹栈,释放空间

    栈中存储:方法执行中需要的内存,以及该方法的局部变量
     */
public class test01 {
    //主方法入口
    public static void main(String[] args){
        System.out.println("main begin");
        /*
        int a = 100;
        int b = a;
        这里的原理是:将a变量中保存的100这个数字复制一份传给b,
        所以a和b是两个不同的内存空间,是两个局部变量
         */
        int x = 100;
        m1(x);
        System.out.println("main begin");
    }

    public static void m1(int i){ //i是局部变量
        System.out.println("m1 begin");
        m2(i);
        System.out.println("m1 over");

    }
    public static void m2(int i){
        System.out.println("m2 begin");
        m3(i);
        System.out.println("m2 over");

    }
    public static void m3(int i){
        System.out.println("m3 begin");
        System.out.println(i);
        System.out.println("m3 over");

    }
}

main begin
m1 begin
m2 begin
m3 begin
100
m3 over
m2 over
m1 over
main begin

image-20210628211831998

方法总结

image-20210629201117417 image-20210629201150478

方法的重载

package Method;
//程序先不使用方法重载,分析程序的缺点?
//程序员需要记忆过多的方法名字
//代码不够美观
public class OverloadTest01 {
    //主方法
    public static void main(String[] args){
        int x = sumInt(10,20);
        System.out.println(x);

        long y = sumLong(20L,10L);
        System.out.println(y);;

        double z = sumDouble(10.00,21.00);
        System.out.println(z);
    }

    public static int sumInt(int a, int b){
        return a+b;
    }
    public  static long sumLong(long a, long b){
        return a+b;
    }
    public static double sumDouble(double a, double b){
        return a+b;
    }
}

package Method;
/*
使用方法重载机制
优点1:代码整齐美观
优点2:可以让功能相似的方法名重合

在java语言中如何进行语言区分?
首先Java编译器首先通过方法名进行区分,但是java语言中允许方法名相同的情况出现
如果方法名相同的情况下,编译器会通过方法的参数类型进行方法的区分
 */
public class overloadTest02 {
    public static void main(String[] args){
        System.out.println(sum(10, 20));
        System.out.println(sum(10l, 20l));
        System.out.println(sum(20.0, 10.0));

    }

    public static int sum(int a,int b){
        System.out.println("int求和");
        return a+b;
    }
    public static long sum(long a,long b){
        System.out.println("long求和");
        return a+b;
    }
    public static double sum(double a,double b){
        System.out.println("double求和");
        return a+b;
    }
}

什么时候会发生方法重载?

在一个类当中功能一和功能二它们的功能是相似的;可以考虑他们的方法名是一致的,这样代码既美观,又便于后期编写

什么时候代码会发生方法重载?

  • 条件一:在同一个类当中
  • 条件二:方法名相同
  • 条件三:参数列表不同
    • 参数的类型不同;参数的个数不同;参数的顺序不同

注意:

  • 方法重载和方法的返回值类型无关
  • 方法重载和方法的修饰符列表无关
package Method;
/*
什么时候会发生方法重载?
在一个类当中功能一和功能二它们的功能是相似的
可以考虑他们的方法名是一致的,这样代码既美观,又便于后期编写
什么时候代码会发生方法重载?
条件一:在同一个类当中
条件二:方法名相同
条件三:参数列表不同
    参数的类型不同;参数的个数不同;参数的顺序不同

 */
/*
大家是否承认println()是一个方法名字
 */
public class overloadTest03 {
    public static void main(String[] args){
        //println是方法名字,是由sun公司写的,可以直接使用
        //println()方法肯定是重载了
        //对于println只需要记住一个方法名就可以了
        //参数类型可以随便传,这说明println()重载了
        System.out.println(10);
        System.out.println("12");
        System.out.println(true);

    }
}

方法递归

image-20210630145514588
package Method;
/*
    1.什么是方法递归?
        方法自己调用自己,就是方法的递归
    2.当递归时,程序没有结束条件,一定会发生,栈溢出错误
    	所以递归一定要结束条件
    3.
 */
public class RecursionTest01 {
    public static void main(String[] args){
        doSome();
    }
    public static void doSome(){
        System.out.println("doSome begin");
        //调用方法:doSome()既是一个帆帆发,那么doSome方法可以调用吗?当然可以
        //目前这个方法是没有停止条件的,电脑会出现什么问题?
        doSome();
        //这行永远执行不到
        System.out.println("doSome over");
    }

}

代码会出现栈的内存溢出错误

1.什么是方法递归?

​ 方法自己调用自己,就是方法的递归

2.当递归时,程序没有结束条件,一定会发生,栈溢出错误所以递归一定要结束条件

3.递归假设有结束条件,就不会发生栈溢出错误么?

​ 假设结束条件是对的,是合法的,递归有时候也会出现栈内存错误,因为有可能递归太深,占内存不够了,因为一直在压栈。

4.在实际的开发中不建议轻易的使用递归,能使用for循环while循环代替的,尽量使用循环来做,因为循环的效率高,耗费的内存少递归耗费的内存比较大,另外递归地使用不当可能会导致JWM死掉(但在极少数的情况下,不用递归程序没有办法实现)

5.在实际的开发中,如果遇到这个栈溢出的错误,如何解决这个问题?

  • 第一步:先检查递归的结束条件是否正确
  • 第二步:递归条件正确,这个时候需要手动调整JVM的栈内存初始化大小可以将栈内存的空间大一些。
  • 第三步:调整了大小,如果运行时还是出现了错误,只能继续扩大栈内存的大小。
  • java-x参数可以查看调整堆栈大小的参数
package Method;
/*
不用递归 计算1-n的和
 */
public class RecursionTest02 {
    public static void main(String[] args){
        int N = 100;
        int res = sum(N);
        System.out.println(res);

    }
    public static int sum(int n){
        int result = 0;
        for(int i = 1; i<= n ; i++){
            result += i;
        }
        return result;
    }
}

package Method;

/*
使用递归 求1-n的和
 */
public class RecursionTest03 {
    public static void main(String[] args){
        System.out.println(sum(100));
    }
    public static int sum(int n) {
        if (n == 1) {
            return 1;
        }
        return n + sum(n - 1);
    }
}

递归内存图

image-20210630155600021
递归求阶乘
package Method;

/*
使用递归 求阶乘
 */
public class RecursionTest03 {
    public static void main(String[] args){
        System.out.println(jicheng(5));

    }
    public static int jicheng(int n){
        if(n ==1 ){
            return 1;
        }
        return n * jicheng(n-1);
    }
}

认识面向对象

1. 面向过程和面向对象有什么区别?

image-20210630163436619 image-20210630163500689 image-20210630163529530 image-20210630164006101

2. 当我们采用面向对象的方式贯穿整个系统的时候,涉及到三个术语:

OOA:面向对象的分析

OOD:面向对象的设计

OOP:面向对象的编程

整个软件开发的过程,都是采用OO进行贯穿的

实现一个软件的过程:

​ 分析A ➡ 设计D ➡ 编程P

3. 面向对象的三大特征

封装、继承、多态

任何一个面向对象的编程语言都包括这三个特征:比如python

注意:java只是面向对象语言中的一种。

4. 类和对象的概念

什么是类?

类实际上在现实世界当中是不存在,是一个抽象的概念,是一个模板,是我们人类大脑进行思考的结果、总结、抽象的一个结果

​ 明星是一个类

什么是对象?(真实存在个体)

对象是实际存在个体,对象还有另外一个名称叫做实例

通过类创建对象的过程,叫做实例化

​ 姚明是一个对象

​ 刘德华是一个对象

​ 这两个对象都是明星这个类

在java语言中,要想得到“对象”,必须先定义“类”,“对象”就是通过“类”,这个模板创造出来的

​ 类就是一个模板:类中描述的是所有对象的“共同特征信息”

​ 对象就是通过类创建出来的个体

image-20210701142048699 image-20210701142234900
类 ——[实例化] ——> 对象(实例)
对象 ——[抽象] ——> 类
类的共同特征

类是一个模板,是描述一个共同特征的模板,那么共同特征是什么?

​ 状态特征、动作特征

​ 类= 属性+ 方法

  • 属性来源于:状态
  • 方法来源于:动作

5. java软件工程师在开发中起到的作用是什么?

​ java是转换虚拟世界和现实世界的桥梁

image-20210701144313658

6. 类的定义

怎么定义一个类,语法格式是什么?
[修饰符列表] class 类名{

	//类= 属性 + 方法

	//属性在代码上以“变量”的形式存在

	//方法描述动作;属性描述状态

}
为什么属性以“变量”的形式存在?

属性对应的是数据,数据只能放到变量中

结论:属性就是变量

变量:根据出现的位置进行划分:

  • 方法体中声明的变量:局部变量
  • 方法体外声明的变量:成员变量
package lei;
/*
1.学生的共同特征:
        学号int、姓名String、性别char、住址String

        注意:属性是成员变量

 */

//Students既是一个类名又是一个类型名,属于引用数据类型
public class Students {
    //属性
    //成员变量
    int xuehao;
    String name;
    int age;
    boolean gender;
    String adress;

}

package lei;
/*
对象的创建和使用
创建对象的语法是什么?
 */
public class StudentTest {
    public static void main(String[] args){
        //可以可以使用Student类的
        /*
        创建对象的语法;new
        new 类名();new是一个运算符,专门负责对象的创建
        数据类型 变量名 = new 类名();

        数据类型包括两种:基本数据类型和引用数据类型
        java中所有的”类“都是引用数据类型
         */
        Students s = new Students();
        //Students是一个引用数据类型
        //new Students() 是一个对象
    }
}

7. 编译的过程

image-20210701153528807

对象的创建与使用

对象的创建

类名 变量名 = new 类名();

这样就完成了对象的创建

变量必须先声明,在赋值才能访问
注意:对于成员变量来说,没有手动赋值 ,系统自动赋值,为默认值

​ 类型 默认值
​ byte 0
​ short 0
​ long 0L
​ int 0
​ float 0.0F
​ double 0.0
​ boolean flase
​ char \u000
​ 引用数据类 null

什么是实例变量

实例是对象,对象又被称为实例;

实例变量实际上就是:对象级别的变量

堆内存中存储对象,以及对象的实例变量

实例变量在访问的时候,是不是必须先创建对象?
//访问学生姓名可以直接通过类名嘛?
//学生姓名是一个实例变量,实例变量是对象级别的变量
//是不是先有对象才可以有姓名
//不能通过类名直接访问实例变量
//System.out.println(Students.name);

对象和引用的区别?

对象是通过new出来,在堆内存中存储

引用是:但凡是变量,并且该变量中保存了内存地址指向了堆内存当中的对象的叫做引用

引用是:存储对象内存地址的一个变量

通俗一点说:只要这个变量中保存的是一个对象的内存地址,这个变量就叫做引用

对象是:堆里new出来的

image-20210701162554542

访问id不能使用User.id,这是错误的,实例变量不能用类名访问。id的访问必须是先造对象,然后有了对象,才能访问对象的id

思考:引用一定是局部变量吗? 不一定是。

JVM中的三个主要内存

栈:主要存储局部变量以及类对象的地址,方法执行时所需要的空间局部变量;只要是方法体中的局部变量,都在栈中进行分配

堆内存:主要存储对象以及对象的实例变量实例变量

方法区:主要存储代码片段

内存图例子:

image-20210704092740772 image-20210704093303785

例子2:

image-20210704102315945
package lei;
/*
记住:里面有什么就能点.什么
所有实例变量都是通过”引用.“来访问的
 */
public class Test01 {
    public static void main(String[] args){
        Address a = new Address();
        a.city = "北京";
        a.street = "大兴区";
        a.zipcode = "10010";
        System.out.println(a.city);
        System.out.println(a.street);
        System.out.println(a.zipcode);

        Users user1 = new Users();
        user1.add = a;
        user1.id = 123;
        user1.name = "yxy";
        /*System.out.println("============”=“代表赋值运算===============");
        int x = 100;
        int y = x;
        表示把x变量中保存的值100复制一份给y
         */
        /*
        Add K = new Add();
        Add m = k;
        Add k = 0x1111;
        那么 m = 0x1111;
         */
        //在java中只有值进行传递,值可能是变量、地址等等
        System.out.println(user1.id);
        System.out.println(user1.name);
        System.out.println(user1.add.city);
        System.out.println(user1.add.zipcode);
        System.out.println(user1.add.street);
    }
}
/*
*引用是:存储对象内存地址的一个变量**
通俗一点说:只要这个变量中保存的是一个对象的内存地址,这个变量就叫做引用
*对象是:堆里new出来的**
*
* 思考:引用一定是局部变量吗?
*  不一定是
 */
package lei;

public class Address {
    String city;
    String street;
    String zipcode;
}

package lei;

public class Users {
    int id;
    String name;
    Address add;

}

属性的引用

package lei;

public class T {
    A a;
    public static void main(String[] args){
        A a = new A();
        B b = new B();
        C c = new C();
        D d = new D();
        T t = new T();

        c.d = d;
        b.c = c;
        a.b = b;
        t.a = a;
        System.out.println(t.a.b.c.d.i);
    }

}
class A{
    B b;
}
class B{
    C c;
}
class C{
    D d;
}
class D{
    int i = 100;
}

空指针

package lei;
/*
空指针异常
 */
public class NullPointerTest {
    public static void main(String[] args){
        //创建客户对象
        Customer c = new Customer();
        //访问这个对象的id
        System.out.println(c.id);
        //重新给id赋值
        c.id = 45;
        System.out.println(c.id);

        c =null;
        /*
        编译器没问题,因为编译器只检查语法,编译器发现c是Customer类型,Customer类型中有id属性,所以可以
        但是运行的时候需要对象的存在,但是对象没了,只能抛出一个异常
         */
        System.out.println(c.id);
        /*
        空指针错误
        Exception in thread "main" java.lang.NullPointerException
	    at lei.NullPointerTest.main(NullPointerTest.java:16)
         */

    }
}
class Customer{
    int id; //成员变量中的实例变量,应该先创建对象,然后通过”引用.“的方式访问
}
image-20210704143229819

关于垃圾回收器GC,在java语言中,java回收器主要针对的是堆内存,当一个java对象没有任何引用指向该对象的时候,GC会考虑将垃圾数据释放被回收。

出现空指针异常的条件是:”空引用“访问实例相关数据时,都会出现空指针异常

方法调用时的参数传递

java中规定:参数传递的时候,和类型无关,无论是基本类型还是引用数据类型,同意都是盒子中保存的值,复制一份传递下去

package canshuchuandi;
//分析程序的参数传递
//java中规定:参数传递的时候,和类型无关,无论是基本类型还是引用数据类型,同意都是盒子中保存的值,复制一份传递下去

public class Test01 {
    public static void main(String[] args){
        int i = 10;
        add(i);
        System.out.println("main-->" + i); //10

    }
    public static void add(int i){ // i在add域中
        //把main中i变量盒子中保存的10这个数复制了一份,传给了add方法
        i++;
        System.out.println("add-->" + i); //11
    }
}

image-20210704150013728
package canshuchuandi;

public class Test02 {
    public static void main(String[] args) {
        Person p = new Person();
        p.age = 10;
        add(p);
        System.out.println("main -->" + p.age); //11
    }
    //方法可以是基本数据类型,也可以是引用数据类型,只要是合法的数据类型就可
    public static void add(Person p){
        p.age++;
        System.out.println("add -->" + p.age); //11
    }
}

class Person{
    int age;
}
image-20210704152041150

这两个p是不同的局部变量

构造方法Constructor

1.什么是构造方法?有什么用?

构造方法是一个比较特殊的方法,通过构造方法可以完成对象的创建以及实例变量的初始化,

换句话说构造方法是用来创建对象并且给对象的属性赋值的(注意:实例变量没有手动赋值的时候,系统会赋默认值)

2.当一个类没有提供任何构造方法,但是系统实际上会默认提供一个无参数的构造方法,(这个方法会被称为缺省构造器)
3.调用构造方法如何使用呢?

使用new运算符来构造方法;语法格式:new 构造方法名(实际参数列表)

4.构造方法的语法结构是什么?
[修饰符列表] 构造方法名(形式参数列表){
    构造方法体;
 }

注意:1.修饰符列表目前统一写public 千万不要写 public static

2.构造方法名和类名必须一致

3.构造方法名不需要指定任何返回值类型,不需要写void

普通方法的语法结构

 [修饰符列表] 返回值类型 方法名(形式参数列表){
    方法体;
}
5.无参数的构造方法和有参数的构造方法
  1. 当一个类中没有提供任何构造方法,系统默认提供一个无参数的构造方法,这个无参数的构造方法叫做缺省构造器。

  2. 如果一个类中手动的提供了构造方法,那么系统将不会提供无参构造方法了,建议手动将无参构造方法写出来,这样一定不会出问题

  3. 无参数的构造方法和有参数的构造方法都可以调用

  4. 构造方法支持方法重载么?

    可以支持方法重载,在一个类中构造方法可以有多个,并且所有的构造方法的名字都是一样的

    方法重载有一个特点:

    ​ 在同一个类中,方法名字相同,参数列表不同

  5. 对于实例变量来说,只要你在构造方法中没有手动赋值,那么系统就会默认赋值

  6. 构造方法的作用:创建对象,并给属性赋值

package Constructor;
/*
构造方法:
    1.什么是构造方法?有什么用?
    构造方法是一个比较特殊的方法,通过构造方法可以完成对象的创建以及实例变量的初始化,
    换句话说构造方法是用来创建对象并且给对象的属性赋值的(注意:实例变量没有手动赋值的时候,系统会赋默认值)
    2.当一个类没有提供任何构造方法,但是系统实际上会默认提供一个无参数的构造方法,(这个方法会被称为缺省构造器)
    3.调用构造方法如何使用呢?使用new运算符来构造方法
    语法格式:new 构造方法名(实际参数列表)
    4.构造方法的语法结构是什么? 没有返回值类型 不能写void
    [修饰符列表] 构造方法名(形式参数列表){
        构造方法体;
     }
     注意:1.修饰符列表目前统一写public 千万不要写 public static
          2.构造方法名和类名必须一致
          3.构造方法名不需要指定任何返回值类型,不需要写void
     普通方法的语法结构
     [修饰符列表] 返回值类型 方法名(形式参数列表){
        方法体;
     }

 */
public class Test01 {
    public static void main(String[] args){

        //调用无参数构造方法
        new Students();

        //调用普通方法
        Test01.doSome();
        doSome();

        //调用Students类的无参数构造方法

        //创建students类型的对象
        Students s1 = new Students();
        System.out.println(s1); //输出哈希值

        //调用有参数的构造方法
        Students s2 = new Students(100);
        System.out.println(s2);

    }
    //普通构造方法
    public static void doSome(){
        System.out.println("ds");
    }
}
class Students{
    int no;
    int age;
    String name;
    //当Students没有构造方法时,系统实际上会默认提供一个无参数的构造方法

    //构造方法
    public Students(){
        System.out.println("无参数执行");
    }

    public Students(int no) {
        this.no = no;
    }
    //重载
    //public void Students(String name){}
    public Students(String name){
        this.name = name;
    }
}

调用不同构造方法创造对象

package Constructor;

public class Test03 {
    public static void main(String[] agrs){
        //调用不同的构造方法创建对象
        Vip v1 = new Vip();
        Vip v2 = new Vip(111,"y");
        Vip v3 = new Vip(222,"x","123");
        Vip v4 = new Vip(333,"y","123",true);
        System.out.println(v1.no);
        System.out.println(v1.name);
        System.out.println(v1.birth);
        System.out.println(v1.sex);
        System.out.println("----------------------");
        System.out.println(v2.no);
        System.out.println(v2.name);
        System.out.println(v2.birth);
        System.out.println(v2.sex);
        System.out.println("----------------------");
        System.out.println(v3.no);
        System.out.println(v3.name);
        System.out.println(v3.birth);
        System.out.println(v3.sex);
        System.out.println("----------------------");
        System.out.println(v4.no);
        System.out.println(v4.name);
        System.out.println(v4.birth);
        System.out.println(v4.sex);

    }
}

0
null
null
false
----------------------
111
y
null
false
----------------------
222
x
123
false
----------------------
333
y
123
true

Process finished with exit code 0

面向对象的三大特征-封装、继承、多态

有了封装才有继承,有了继承,才有多态

封装

什么是封装?有什么用?

​ 现实生活中有很多现实的例子都是封装的,例如:手机、电视机等

​ 封装起来,保护内部的部件,另外封装之后, 对于我们使用者来说,我们是看不见内部的复杂结构的。

封装的作用

一个类体当中的数据,假设封装之后,对于代码的调用人员来说,不需要关心代码的复杂实现,是需要通过一个简单的入口就可以访问了,另外,类体中安全级别较高的数据封装起来 ,外部人员不能随意访问,来保证数据的安全性。

怎么进行封装

第一步:属性私有化,private、

第二步:对外提供简单的操作入口,对外提供公开的set方法和get方法,并且这两个方法都不带有static,为对象级别的方法

​ 可以在set方法中设立关卡,来保护数据的安全性

注意:java开发规范中,set方法和get方法要满足以下格式 不可以带static

get方法的要求:
    public 返回值类型 get+属性名首字母大写(无参){
        return xxx;
    }
set方法的要求:
    public void+属性名首字母大写(有一个参数){
        xxx = 参数;
    }

无封装的情况:

package fengzhuang;
//在外部程序中访问Person
public class PersonTest {
    public static void main(String[] args){
        //创建Person对象
        Person p1 = new Person();
        //访问对象的属性,一般分为两种操作,一种是读数据,一种是改数据
        p1.age = 50;
        System.out.println(p1.age);

        //在Person这个外部程序中,目前可以随意对age进行操作
    }
}
class Person {
    //实例变量、属性
    int age;

}

有封装的情况:

package fengzhuang;
//在外部程序中访问Person
public class PersonTest {
    public static void main(String[] args) {
        //private 表示私有的,被这个关键字修饰之后,该数据只能在本类中访问,除了这个类,这个属性将不能被访问
        /*
        Person age,彻底在外部不能进行访问
        p1.age = 20;
        //读取
        System.out.println(p1.age);
         */
        Person p1 = new Person();
        //通过类名.不可以调用get和set方法。
        p1.setAge(-1);
        int age = p1.getAge();
        System.out.println(age);

    }
}

package fengzhuang;

//这是没有封装的Person
/*

public class Person {
    //实例变量、属性
    int age;

}
*/
//开始封装代码,不在对外暴露复杂的数据
//封装起来,保护内部的数据,保证其安全性
public class Person{
    //属性私有化
    private int age;
    //对外提供简单的操作入口
    /*
    写专门的方法来完成读写(get、set)
    对外提供公开的set方法和get方法,并且这两个方法都不带有static,为对象级别的方法
     */
    //注意:java开发规范中,set方法和get方法要满足以下格式
    /*
    get方法的要求:
        public 返回值类型 get+属性名首字母大写(无参){
            return xxx;
        }
    set方法的要求:
        public void+属性名首字母大写(有一个参数){
            xxx = 参数;
        }
     */
    public int getAge(){
        return age;
    }

    public void setAge(int age) {
        if(age <0 || age >150){
            System.out.println("年龄不合法");
            return; //程序终止
        }
        this.age = age;
    }
}

static关键字

静态变量在类加载时进行初始化
  1. 所有static关键字修饰的都是类相关的,类级别的
  2. 所有static修饰的,都是采用“类名."的方式访问的
  3. static修饰的变量:静态变量
  4. static修饰的方法:静态方法

变量的分类:

​ 变量根据声明的位置进行划分:在方法体内的变量叫做:局部变量;

​ 在方法体外的变量叫做:成员变量。

​ 成员变量又可以分为:

​ 实例变量

​ 静态变量

1.当county不是静态变量的时候:会导致内存的浪费,country放在堆中
image-20210706160537684
package Staticpackage;
/*
什么时候声明为静态的,什么时候声明为实例的
 */
public class Test02 {
    public static void main(String[] args){
        Chinese c1 = new Chinese("123456","yxy");
        Chinese c2 = new Chinese("123456789","yxa");

    }
}
class Chinese{
    //身份证号应该为实例变量,一个人一份
    //姓名也应该是实例变量
    String idCard;
    String name;
    //国籍是一个固定值,所以是静态变量
    String country;

    public Chinese() {
    }

    public Chinese(String idCard, String name) {
        this.idCard = idCard;
        this.name = name;
    }
}
2.当country为静态变量时,静态变量放在方法区当中
image-20210706165834075
package Staticpackage;
/*
什么时候声明为静态的,什么时候声明为实例的
如果这个对象的某个属性值都是一样的,不建议定义为实例变量,建议定义为类级别特征,定义为静态变量,在方法区中保留,节省开销
 */
public class Test02 {
    public static void main(String[] args){
        //静态变量的输出
        System.out.println(Chinese.country);
        Chinese c1 = new Chinese("123456","yxy");
        Chinese c2 = new Chinese("123456789","yxa");
        System.out.println(c1.idCard);
        System.out.println(c1.name);
        System.out.println(c2.idCard);
        System.out.println(c2.name);

        //System.out.println(Chinese.idCard);
        //程序报错,因为idCard时实例变量,必须通过引用.访问

    }
}
class Chinese{
    //身份证号应该为实例变量,一个人一份
    //姓名也应该是实例变量
    String idCard;
    String name;

    //声明为静态变量也具有初始值,为null,这个初始值在类加载时进行初始化
    //并且静态变量存在方法区
    //国籍是一个固定值,所以是静态变量
    static String country = "中国";

    public Chinese() {
    }

    public Chinese(String idCard, String name) {
        this.idCard = idCard;
        this.name = name;
    }
}
3. 关于空指针异常
package Staticpackage;
/*
 实例的:应引用.来访问
 静态的:建议使用”类名.“来访问

 结论:
   空指针异常只有在”空引用“访问”实例“相关的,都会出现空指针异常
 */
public class Test03 {
    public static void main(String[] args){
        System.out.println(Chinese.country);
        //创建对象
        Chinese c1 = new Chinese("11", "yxy");
        System.out.println(c1.country);
        System.out.println(c1.name);
        System.out.println(c1.idCard);
        //空引用
        c1 = null;
        //不会出现空指针异常,因为静态变量不需要对象的存在,这个代码实际上还是System.out.println(Chinese.country);
        System.out.println(c1.country);

    }

}
class Chinese{
    String idCard;
    String name;

    public Chinese() {
    }

    static String country = "中国";
    public Chinese(String x, String y){
        idCard = x;
        name = y;
    }
}
4. 静态方法的使用
package Staticpackage;

public class Test04 {
    public static void main(String[] args){
        Test04.dosome();
        Test04 st = new Test04();
        st.doOther();

    }
    //静态方法不需要new对象,采用类名.
    public static void dosome(){
        System.out.println("静态方法do some");

    }
    //实例方法都需要new对象,使用”引用.“来访问
    public void doOther(){
        System.out.println("实例方法");
    }
}

5. 类中的东西
image-20210706194452467
6. 方法什么时候定义为静态的

参考标准:当这个方法体当中,访问了实例变量,那么一定是实例方法。大部分情况下,如果是工具类的方法一般都是静态的(静态方法不需要new对象,直接采用类型调用,极其的方便,工具类就是为了方便,所以工具类中的方法一般为static)实例方法的定义:不同对象参加考试的结果不同,我们可以认为考试是与对象相关的东西,那么我们把考试定义为实例方法

package Staticpackage;
/*
什么时候定义为实例方法,什么时候定义为静态方法
 参考标准:当这个方法体当中,访问了实例变量,那么一定是实例方法
        大部分情况下,如果是工具类的方法一般都是静态的(静态方法不需要new对象,直接采用类型调用
        极其的方便,工具类就是为了方便,所以工具类中的方法一般为static)

        实例方法的定义:不同对象参加考试的结果不同,我们可以认为考试是与对象相关的东西,那么我们把考试定义为实例方法

 */
//类 = 属性 + 方法
public class Test05 {
    public static void main(String[] args){

    }
}
class User{
    //实例变量,需要对象r
    private int id;
    private String name; //首先name是对象级别的。
    //打印用户的名字,是实例方法
    public void printName(){

    }
    // 先new一个对象才可以get和set方法
    public void setId(int i){
        id = i;
    }
    public int getId(){
        return id;
    }
}
7. 不可以在main方法中定义static变量的原因

只有类才存在静态的变量,方法只能对静态变量的操作,不能在方法内试图定义静态变量

static静态块

  1. static可以定义静态代码块
语法结构:
    static{
        java语句;
        java语句;
        }
  1. 执行时间:类加载时执行,并且只执行一次

  2. 注意:一个类当中可以写多个静态代码块,静态代码块在类加载时执行,并且在main方法执行前执行,静态代码块一般按照自上而下的顺序执行

  3. 静态代码块的作用:记录项目日志

    特殊的时机:类加载的时机,放代码

package Staticpackage;
/*
static可以定义静态代码块
语法结构:
    static{
        java语句;
        java语句;
        }
执行时间:
       类加载时执行,并且只执行一次
特点:
    一个类当中可以写多个静态代码块,静态代码块在类加载时执行,并且在main方法执行前执行,静态代码块一般按照自上而下的顺序执行**
 */
public class Test06 {

    //静态代码块
    static{
        System.out.println("A");

    }
    //一个类中可以定义多个静态代码块
    static{
        System.out.println("B");

    }
    public static void main(String[] agrs){
        System.out.println("hello word");
    }
    static{
        System.out.println("C");

    }

}

静态变量在类加载时初始化,静态代码块在类加载时执行,所以静态代码块可以访问静态变量,

实例变量的初始化是在new对象的时候开始,在构造方法执行时内存空开才会开辟

总结:栈、堆、方法区

栈:只要方法执行,就会进行压栈操作,栈会提供方法所需要的空间,栈内存放局部变量

堆:堆内存放实例变量,new出来的对象

方法区:类的信息、存放代码片段和静态变量

到目前为止,有顺序要求的执行代码有哪些?
image-20210706204825231
实例语句块
package Staticpackage;

public class InstanceCode {
    //入口
    public static void main(String[] args){
       new InstanceCode();
       new InstanceCode();
       new InstanceCode("abc");
    }

    //实例语句块 不在类加载时执行
    {
        System.out.println("实例语句执行");
    }

    public InstanceCode() {
        System.out.println("无参数构造");
    }
    public InstanceCode(String name) {
        System.out.println("有参数构造");
    }

}

实例语句块,不在类加载时执行,在构造器执行之前执行,并且在每一次new对象时,都执行一次

所学的各个代码块的执行顺序
package Staticpackage;
//判断执行顺序
public class CodeOrder {
    static{
        System.out.println("A");
    }
    public static void main(String[] args){
        System.out.println("main begin");
        new CodeOrder();
        System.out.println("main over");
    }

    public CodeOrder() {
        System.out.println("B");
    }

    {
        System.out.println("C");
    }
    static{
        System.out.println("X");
    }
}

A
X
main begin
C
B
main over

this关键字

  1. this 是一个关键字,全部小写

  2. 一个对象一个this,this是一个变量,是一个引用。this保存当前对象的内存地址,指向自身,所以,严格意义上来说,this代表的是“当前对象”,this存储在堆内存当中对象的内部。

  3. this只能使用在实例方法中。谁调用这个实例方法,this就是谁,所以this代表的是当前对象。

  4. this.大部分情况下是可以省略的

  5. 为什么this不可以使用在静态方法中?

    this代表的是当前对象,而静态方法的调用不需要对象

  6. 在实例方法中,或者构造方法中,为区分局部变量和实例变量,这种情况下this是不可以省略的

    //this不可以省略的情况
    
    package Staticandthis;
    /*
    this 可以使用在实例方法中,不能使用在静态方法中,this关键字大部分情况下可以省略
         在实例方法中,或者构造方法中,为区分局部变量和实例变量,这种情况下this是不可以省略的
     */
    public class Test09 {
        public static void main(String[] args){
            System.out.println("——————无参数构造——————");
            Students s = new Students();
            s.setName("yxy");
            s.setNo(1);
            System.out.println(s.getName());
            System.out.println(s.getNo());
            System.out.println("——————有参数构造——————");
            Students s2 = new Students(2,"yx");
            System.out.println(s2.getName());
            System.out.println(s2.getNo());
        }
    }
    class Students{
        private int no;
        private String name;
    
        public Students() {
        }
            //this.no是一个实例变量
            // no是一个局部变量
        public Students(int no, String name) {
            this.no = no;
            this.name = name;
        }
    
        public void setNo(int no) {
            this.no = no;
        }
    
        public void setName(String name) {
            this.name = name;
        }
            //getName实际上获取的是当前名字的
        public int getNo() {
            return no;//return this.name
        }
    
        public String getName() {
            return name;
        }
    }
    

image-20210707162018274

package Staticandthis;
/*
  1.this是一个关键字,全部小写
  2.this是什么,在内存方面是怎么样的?
    this是一个变量,是一个引用,this保存当前对象的内存地址,指向自身
    一个对象一个this
    所以严格意义上来说,this代表的就是“当前对象”,this存储在堆内存当中
 */
public class Test08 {
    public static void main(String[] args){
        Customer c1 = new Customer("yxy");
        c1.shopping(); //this 代表c1,c1调用shopping,this就是c1
        Customer c2 = new Customer("x");
        Student.m();
    }
    
}
class Customer {
    String name;

    public Customer() {
    }

    public Customer(String s) {
        name = s;
    }

    //顾客购物方法 实例方法
    public void shopping() {
        //c1调用shopping,this就是c1
        //c2调用shopping,this就是c2
        System.out.println(this.name + "正在购物");//引用.就是this. this.是可以省略的
    }

    public static void doSome() {
        //this代表的是当前对象,而静态方法的调用不需要对象
        // System.out.println(this);
    }
}
class Student{
    //实例变量,必须new对象,通过引用.来访问
    String name = "z";
        //所以name不能访问
    public static void m(){
        // System.out.println(name);
        //可以这样
        Student s = new Student();
        System.out.println(s.name);

        //方法中直接访问了实例变量,那么必须是实例方法
    }
}
yxy正在购物
z
  1. 新语法:通过当前的构造方法去调用另一个本类的构造方法,可以使用以下语法格式:

    this(实际参数列表);

    通过一个构造方法1去调用构造方法2,可以做到代码复用,但是需要注意的是:构造方法1和构造方法2都是在同一个类当中

    新语法的作用是:代码复用

    在构造方法中:this()前面不可以有其他的语句,并且只可以出现一次。

    package Staticandthis;
    /*
    this` 可以用在实例方法以外,还可以用在构造方法中
    新语法:通过当前的构造方法去调用另一个本类的构造方法,可以使用以下语法格式:this(实际参数列表);
     */
    public class Test10 {
        public static void main(String[] args) {
            Date d1 = new Date();
            d1.setDay(10);
            Date d2 = new Date(2000, 01, 01);
            d1.detail();
            d2.detail();
    
        }
    }
    class Date{
        private int year;
        private int month;
        private int day;
    
        public Date(){
            //错误:对this 的调用必须是构造器中的第一个语句
            // System.out.println(11);
           /* this.year = 1970;
            this.month = 1;
            this.day = 1;*/
            this(1970,1,1);
        }
    
        public Date(int year, int month, int day) {
            this.year = year;
            this.month = month;
            this.day = day;
        }
        //提供一个打印方法
        public void detail(){
            System.out.println(year + "," + month + "," + day);
    
        }
        public void setYear(int year) {
            this.year = year;
        }
    
        public void setMonth(int month) {
            this.month = month;
        }
    
        public void setDay(int day) {
            this.day = day;
        }
    
        public int getYear() {
            return year;
        }
    
        public int getMonth() {
            return month;
        }
    
        public int getDay() {
            return day;
        }
    
    }
    
总结
image-20210707210850280
package Staticandthis;

public class Test11 {
    public static void main(String[] args){
        //创建账户
        Account a = new Account("1000",2000,1.23);
        //创建客户对象
        //传a是想让Customer和Account对象产生关系
        //把a的内存地址直接赋值给变量act
        Customers c = new Customers("yx",a);


        c.getAct().deposit(100);
        c.getAct().withdraw(960);
        c.getAct().withdraw(2000);

    }
}
//账户类
class Account{
    private String id;
    private double balacen;
    private double annualInterestRate;

    public Account() {
    }

    public Account(String id, double balacen, double annualInterestRate) {
        this.id = id;
        this.balacen = balacen;
        this.annualInterestRate = annualInterestRate;
    }
    public void setId(String id) {
        this.id = id;
    }

    public void setBalacen(double balacen) {
        this.balacen = balacen;
    }

    public void setAnnualInterestRate(double annualInterestRate) {
        this.annualInterestRate = annualInterestRate;
    }

    public String getId() {
        return id;
    }

    public double getBalacen() {
        return balacen;
    }

    public double getAnnualInterestRate() {
        return annualInterestRate;
    }



    //存款方法
    public void deposit(double money){
        if(money > 0){
           // balacen = money + balacen;
           // return balacen;
           // 调用方法来修余额
            setBalacen(getBalacen() + money);
            System.out.println("成功存入" + money);
        }else {
            System.out.println("请输入正确金额");
            return;
        }
    }
    //取款方法
    public void withdraw(double money){
        if(money < getBalacen()){
            //balacen = balacen -money;
            //return balacen;
            setBalacen(getBalacen() - money);
            System.out.println("成功取出" + money);
        }else {
            System.out.println("余额不足");
            return;
        }
    }
}


class Customers{
    private String name;
    private Account act;

    public Customers() {
    }

    public Customers(String name, Account act) {
        this.name = name;
        this.act = act;
    }

    public String getName() {
        return name;
    }

    public Account getAct() {
        return act;
    }

    public void setName(String name) {
        this.name = name;
    }

    public void setAct(Account act) {
        this.act = act;
    }
}

java三大变量

image-20210707200640995

总结:

所有的变量如何访问

所有的方法如何访问

一个类中都有什么

程序无论怎么变化,有一个固定的规律
所有的实例相关的都是先创建对象,通过”引用.“来访问
所有的静态相关的都是直接采用”类名.“来访问

结论:
只要是负责调用的方法a和被调用的方法b在同一个类当中:
this.
类名. 可以省略

package Staticandthis;
/*
类体{
    实例变量;
    实例方法;

    静态变量;
    静态方法;

    构造方法;

    静态代码块;
    实例语句块;

    方法(){
     局部变量;
     }
}
 */
public class Review {
    //在程序执行之前,将所有的类全部加载到JVM中
    //先完成加载才会执行main方法
    static{
        System.out.println("Review类加载");
    }
    //静态方法
    public static void main(String[] args){
        //局部变量
        int i = 100;
        //完成全部的动作
        stu s1 = new stu();
        s1.study();




    }
}
//学生类
class stu{
    private int no;
    private String name;
    static String job = "学习";
    {
        System.out.println("实例语句块,这个构造方法执行一次,这里就执行一次");
    }

    public stu(int no, String name) {
        this.no = no;
        this.name = name;
    }

    public stu() {
        //默认学生的学号和姓名
        this(100,"yxy");
    }
    //提供两个实例方法
    //在实例方法中调用本类其他的实例方法
    public void study(){
        //System.out.println(this.getName() + "在努力学习");
        System.out.println(name + "在努力学习");
        //this.可以省略
        //eat();
        this.eat();
    }
    public void eat(){
        System.out.println(name + "在餐厅吃饭");
        //静态方法使用类名.的方式
        //在同一个类当中,类名.可以省略
        stu.m1();


    }
    //提供两个静态方法
    public static void m1(){
        System.out.println("M1 method");
        m2();
    }
    public static void m2(){
        System.out.println("M2 method");
        System.out.println("状态" + job);//stu.job

    }

    public int getNo() {
        return no;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}
package Staticandthis;
/*
程序无论怎么变化,有一个固定的规律
   所有的实例相关的都是先创建对象,通过”引用.“来访问
   所有的静态相关的都是直接采用”类名.“来访问

   结论:
   只要是负责调用的方法a和被调用的方法b在同一个类当中:
    this.
    类名. 可以省略
 */
public class Review02 {
    int i = 100;
    static int j = 1000;
    public void m1(){}
    public void m2(){}
    public void x(){ // 这个方法是实例方法,执行这个方法的过程中,当前对象是存在的
        m1(); //this.m1();
        m2();
        m3();
        m4();
        System.out.println(i);
        System.out.println(j);
        //访问其他类的静态方法
        T.t2();
        //访问其他类的实例方法
        T t = new T();
        t.t1();

    }
    public static void m3(){}
    public static void m4(){}
    /*
    第一步:
        main 方法是静态的,JVM调用main方法的时候直接采用的是”类名.“的方式、所以main方法中没有this
    第二步:
        m1() m2()都是实例方法,按照java语法来说,应该先new对象,在用过”引用.“的方式进行访问
     */
    public static void main(String[] args){
        // System.out.println(i);
        System.out.println(j);
         // m1();
         // m2();
         m3();
         m4();
       //想要访问m1 m2 先new对象
       Review02 r = new Review02();
       r.m1();
       r.m2();
       System.out.println(r.i);

       //局部变量 可以直接访问
        int k = 10;
        System.out.println(k);

        T t = new T();
        t.t1();
        T.t2();


    }
}


class T{
    public void t1(){}
    public static void t2(){}
}

继承

继承的作用:

​ 基本作用:子类继承父类,代码可以得到复用

主要作用:因为有了继承关系,才有了后期的方法覆盖和多态

继承的相关特性:

① B类继承 A类,则称 A类为超类(superclass)、父类、基类,B类则称为子类(subclass)、 派生类、扩展类。

java 中的继承只支持单继承,不支持多继承,C++中支持多继承,这也是 java 体 现简单性的一点,换句话说,java 中不允许这样写代码:

​ class B extends A,C{ }。

③ 虽然 java 中不支持多继承,但有的时候会产生间接继承的效果,

​ 例如:class C extends B,class B extends A,也就是说,C 直接继承 B,其实 C 还间接继承 A。

④ java 中规定,子类继承父类,除构造方法外,剩下都可以继承。但是私有的属性无法在子类中直接访问,可以通过间接的手段

⑤ java 中的类没有显示的继承任何类,则默认继承 Object 类,Object 类是 java 语言提供的根类(老祖宗类),也就是说,一个对象与生俱来就有 Object 类型中所有的特征。

⑥ 继承也存在一些缺点,例如:CreditAccount 类继承 Account 类会导致它们之间耦合度非常高,Account 类发生改变之后会马上影响到 CreditAccount 类

对继承自Object

测试extends

1. 子类继承父类后,可以使用子类对象调用父类方法吗?

本质上,子类继承父类后,是将父类继承过来的方法归自己所有,实际上调用的也不是父类的方法,是子类自己的方法,是属于子类的方法

package Extends;
/*
测试,子类继承父类,可以使用子类对象调用父类对象吗?
    本质上,子类继承父类后,是将父类继承过来的方法归自己所有,实际上调用的也不是父类的方法,是子类自己的方法,是属于子类的方法

 */
public class Test02 {
    public static void main(String[] args){
        Cats c = new Cats();
        c.move();
        System.out.println(c.name);


    }
}
class Animal{
    //名字不封装
    String name = "x";//给一个默认值
    public void move(){
        System.out.println(name + "正在移动");
    }
}
//Cats继承Animal会将所有的东西继承过来
class Cats extends Animal{

}
2. 在实际开发中,满足什么条件是可以使用继承?

凡是采用“is a”能描述的,都可以继承

​ 例如:cat is a animal

假设以后的开发中,有一个A类和一个B类,A类和B类确实有重复的代码,那么他们两个之间就可以继承吗?不一定,还要看一看他们之间是否能够用“is a”来描述

​ class Customer{
​ String name; // 名字
​ // setter and getter
​ }

​ class Product{
​ String name; // 名字
​ // setter and getter
​ }

​ class Product extends Customer{

​ }

以上的继承就属于很失败的。因为:Product is a Customer,是有违伦理的。

3. 任何一个类,没有显示继承任何类,默认继承Object,那么Object中都有什么?

java为什么比较好学呢?
是因为Java内置了一套庞大的类库,程序员不需要从0开始写代码,程序员可以基于这套庞大的类库进行“二次”开发。(开发速度较快,因为JDK内置的这套库实现了很多基础的功能。)

​ 例如:String是SUN编写的字符串类、System是SUN编写的系统类。这些类都可以拿来直接使用。

JDK源代码在什么位置?
C:\Program Files\Java\jdk-13.0.2\lib\src.zip

你现在能看懂以下代码了吗?
System.out.println(“Hello World!”);
System.out 中,out后面没有小括号,说明out是变量名。

另外System是一个类名,直接使用类名System.out,说明out是一个静态变量。

System.out 返回一个对象,然后采用“对象.”的方式访问println()方法

package Extends;
//idea中 蓝色是关键字
//黑色的标识符
//System.out.println("Hello word");
//以上代码中System、out、println都是标识符
public class Test03 {
    static Student s = new Student();
    public static void main(String[] args){
        //入口
        Test03.s.ex();//类.静态变量.方法
        System.out.println("Hello word");

    }
}
class Student{
    public void ex(){
        System.out.println("考试。");
    }
}

Object其中有一个叫做toString()的,我们进行了测试,发现:
System.out.println(引用);
当直接输出一个“引用”的时候,println()方法会先自动调用“引用.toString()”,然后输出toString()方法的执行结果。

package Extends;
/*
默认继承Object,Object中有哪些方法?
package java.lang;

import jdk.internal.vm.annotation.IntrinsicCandidate;

public class Object {


    @IntrinsicCandidate
    public Object() {}

    @IntrinsicCandidate
    public final native Class<?> getClass();


    @IntrinsicCandidate
    public native int hashCode();

    public boolean equals(java.lang.Object obj) {
        return (this == obj);
    }

    @IntrinsicCandidate
    protected native java.lang.Object clone() throws CloneNotSupportedException;


    public String toString() {
        return getClass().getName() + "@" + Integer.toHexString(hashCode());
    }


    @IntrinsicCandidate
    public final native void notify();


    @IntrinsicCandidate
    public final native void notifyAll();


    public final void wait() throws InterruptedException {
        wait(0L);
    }


    public final native void wait(long timeoutMillis) throws InterruptedException;


    public final void wait(long timeoutMillis, int nanos) throws InterruptedException {
        if (timeoutMillis < 0) {
            throw new IllegalArgumentException("timeoutMillis value is negative");
        }

        if (nanos < 0 || nanos > 999999) {
            throw new IllegalArgumentException(
                    "nanosecond timeout value out of range");
        }

        if (nanos > 0 && timeoutMillis < Long.MAX_VALUE) {
            timeoutMillis++;
        }

        wait(timeoutMillis);
    }

    @Deprecated(since="9")
    protected void finalize() throws Throwable { }
}

 */
public class Test05 {
    public static void main(String[] args){
        Test05 t = new Test05();
        String r = t.toString();
        System.out.println(r); //Extends.Test05@1b6d3586   1b6d3586 可以等同看作是对象在堆内存当中的内存地址,实际上是内存地址经过哈希算法得出的结果
        product p = new product();
        String r2 = p.toString();
        System.out.println(r2);
        System.out.println(p.toString());
        //如果直接输出引用呢
        System.out.println(p);//默认调用toString方法

    }
}
class product{
    /*
     public String toString() {
        return getClass().getName() + "@" + Integer.toHexString(hashCode());
    }
     */

}

Extends.Test05@1b6d3586
Extends.product@4554617c
Extends.product@4554617c
Extends.product@4554617c

方法的覆盖和多态机制

方法覆盖

什么时候使用方法覆盖?

当子类继承父类之后,当继承过来的方法无法满足当前子类的业务需求时,子类有权利对这个方法进行重新编写,有必要进行方法的覆盖,方法覆盖又叫做方法重写Override

方法覆盖的条件

条件一:两个类必须具有继承关系

​ 继承的两个作用:基本作用和重要作用
​ 基本作用:代码复用
​ 重要作用:方法覆盖和多态机制

条件二:重写之后的方法和继承过来的方法具有

​ 相同的返回值类型,
​ 相同的方法名,
​ 相同的形式参数列表

条件三:访问权限不能更低,可以更高

条件四:重写之后的方法不能比之前的方法抛出的更多,可以更少

package Override;
/*
什么时候会考虑使用方法覆盖:当子类继承父类之后,当继承过来的方法无法满足当前子类的业务需求时,子类有权利对这个方法进行重新编写,有必要进行方法的覆盖,
方法覆盖又叫做方法重写Override

重要结论:
    当子类对父类继承过来的方法进行方法重写之后,那么创建子类对象,对象调用的一定是覆盖后的方法
回顾以下方法重载:
    什么时候开率使用方法重载?overload
    当在一个类当中,如果功能相似的话,建议将名字定义的一样,这样代码美观,并且方便编程
    什么条件满足方法重载?
        条件一:在同一个类当中
        条件二:方法名相同
        条件三:参数列表不同(个数,顺序,类型)

    当我们代码怎么编写的时候,在代码级别上构成了方法覆盖呢?
        条件一:两个类必须具有继承关系
        继承的两个作用:基本作用和重要作用
            基本作用:代码复用
            重要作用:方法覆盖和多态机制
        条件二:重写之后的方法和继承过来的方法具有
                相同的返回值类型,
                相同的方法名,
                相同的形式参数列表
        条件三:访问权限不能更低,可以更高
        条件四:重写之后的方法不能比之前的方法抛出的更多,可以更少
 */
public class Test02 {
    public static void main(String[] args){
        Brid b =new Brid();
        b.move();
        b.sing(1);

        Cat c = new Cat();
        c.move();

    }
}
class Animal{
    public void move(){
        System.out.println("动物在移动!");

    }
    //父类有抛出异常
    /*public void move() throws Error{
        System.out.println("动物在移动!");
    }*/

    //更低的权限可以被public访问
    /*protected void move(){
        System.out.println("动物在移动!");

    }*/
    public void sing(int i){
        System.out.println("Animal singing");
    }
}
class Brid extends  Animal{
    //对move方法进行方法覆盖,方法重写,override
    //最好将父类中的方法原封不动的复制过来(不建议手动编写)

    //方法覆盖就是将继承过来的方法覆盖掉了
    public void move(){
        System.out.println("鸟在飞行!");

    }
    //子类抛出的异常不能比父类多
    /*public void move() {
        System.out.println("动物在移动!");
    }*/

    //protected表示受保护的,没有public开放
    //错误:访问权限太低,
   /* protected void move(){
        System.out.println("鸟在飞");
    }*/

    //没够构成方法覆盖
    //但是可以构成方法重载
    public void sing(){
        System.out.println("Bird singing");
    }

}
class Cat extends Animal{
    public void move(){
        System.out.println("猫在走");
    }
}
注意事项
  • 注意1:方法覆盖只是针对于方法与属性无关
  • 注意2:私有方法无法覆盖
  • 注意3:构造方法不能被继承,所以构造方法也不能被覆盖
  • 注意4:方法覆盖只能针对于治理方法,静态方法没有覆盖意义
经典的案例
package Override;
//一个方法覆盖比较经典的案例
public class Test03 {
    public static void main(String[] args){
        //第一种方法
        /*Chinese c = new Chinese("中国人");
        c.speak();
        American a = new American("美国人");
        a.speak();*/
        //第二种方法
        Chinese c = new Chinese();
        c.setName("中国人");
        c.speak();
        American a = new American();
        a.setName("美国人");
        a.speak();
    }
}
class People{
    private String name;

    public People() {
    }

    public People(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
    public void speak(){
        System.out.println(name + "正在说话");
    }
}
class Chinese extends People{
    public Chinese(String name) {
        super(name);
    }

    public Chinese() {
    }

    public void speak(){
        //name为私有属性,只有调用grtName()才可以调用name
        System.out.println(this.getName() + "说汉语");
    }
}
class American extends People{

    public American(String name) {
        super(name);
    }

    public American() {
    }

    public void speak(){
        //name为私有属性,只有调用grtName()才可以调用name
        System.out.println(this.getName() + "说英语");
    }
}
package Override;
/*
关于Object中的toString()方法
    1.toString()方法的作用是什么?
        将java对象转换成字符串形式
    2.Object类中toSting()方法的默认实现是什么?
     public String toString() {
          return getClass().getName() + "@" + Integer.toHexString(hashCode());
      }
      toSrting:意思是转换成String
      含义:调用一个Java对象的toString方法就可以将该Java对象转化成字符串形式。
    3.那么toString()方法给的默认实现够用么?

 */
public class Test04 {
    public static void main(String[] args){
        MyDate t1 = new MyDate();
        //将对象转换为字符串形式
        //MyDate@776ec8df 是重写toString方法之前的结果
        //希望输出:xxxx年xx月xx日
        //1970年1月1日 重写之后的输出
        System.out.println(t1.toString());

        //当输出一个引用的时候,println方法会自动调用引用的toString方法
        System.out.println(t1);

        MyDate t2 =new MyDate(2000,8,7);
        System.out.println(t2.toString());
        System.out.println(t2);

    }
}
class MyDate{
    private int year;
    private int month;
    private int day;

    public MyDate() {
        //默认时间
        this(1970,1,1);
    }

    public MyDate(int year, int month, int day) {

        this.year = year;
        this.month = month;
        this.day = day;
    }

    public int getYear() {
        return year;
    }

    public int getMonth() {
        return month;
    }

    public int getDay() {
        return day;
    }

    public void setYear(int year) {
        this.year = year;
    }

    public void setMonth(int month) {
        this.month = month;
    }

    public void setDay(int day) {
        this.day = day;
    }
    public String toString() {
        return year + "年" + month + "月" + day + "日";
    }


}
方法重载和方法覆盖有什么区别?

​ 方法重载发生在同一个类当中。

​ 方法覆盖是发生在具有继承关系的父子类之间。

​ 方法重载是一个类中,方法名相同,参数列表不同。

​ 方法覆盖是具有继承关系的父子类,并且重写之后的方法必须和之前的方法一致:
​ 方法名一致、参数列表一致、返回值类型一致。

多态

向上转型和向下转型的概念

第一个:向上转型
子 --> 父 (自动类型转换)
第二个:向下转型
父 --> 子 (强制类型转换,需要加强制类型转换符)
注意:
Java允许向上转型,也允许向下转型
无论是向上转型,还是向下转型
两种类型之间必须有继承关系,没有继承关系编译器报错

019-向上和向下转型

什么是多态?

多种状态
Java程序分为编译阶段和运行阶段
先来分析编译阶段:
对于编译器来说,编译器只知道a2的类型是Animal
所以编译器在编译的时候,会去Animal.class字节码文件中去找move方法,找到了绑定上move方法,编译成功,静态绑定成功
分析运行阶段:
运行阶段的时候,实际上在堆内存中存在的java对象是Cat对象,所以move的时候,真正参与move的对象是Cat,所以运行阶段会动态执行Cat对象的move()方法,这个过程属于运行阶段的绑定,属于动态绑定

多态表示多种形态:
编译的时候一种形态
运行的时候是一种形态。

向下转型的风险

会产生ClassCastException的错误

怎么避免:ClassCastException的产生
新的内容,运算符:instanceof

​ 第一:instanceof可以在运行阶段动态判断引用指向的对象的类型
​ 第二:引用instanceof的语法:
​ (引用instanceof类型)
​ 第三:instanceof运算符的运算结果只能是:true/fasle
​ 第四:c是一个引用,c变量保存了内存地址,指向了堆中的对象
​ 假设(c instanceof Cat)为True; 表示c内存中的java对象是一个Cat
​ 假设(c instanceof Cat)为false;表示c内存中的java对象不是一个Cat
​ instanceof在运行阶段动态判断。

​ 任何时候任何地点,在对类型进行向下转型时,一定要使用instanceof运算符,可以很好的避免ClassCastException

package duotai;
/*
多态的基础语法
 1.学习多态基础语法之前,我们需要普及两个概念:
    第一个:向上转型
     子 --> 父
    第二个:向下转型
     父 --> 子
    注意:
        Java允许向上转型,也允许向下转型
        无论是向上转型,还是向下转型
        两种类型之间必须有继承关系,没有继承关系编译器报错
   2.多态指的是:
            父类型引用指向子类的对象
            包括编译阶段和运行阶段
            编译阶段:绑定父类型的方法
            运行阶段:动态绑定子类型的方法
   3.什么时候使用向下转型?
        不要随便做强制类型转换
        子类对象中特有的方法,使用向下转型

 */
public class Test01 {
    public static void main(String[] args){
        Animal a1 = new Animal();
        a1.move();
        Cat c1 = new Cat();
        c1.move();
        Bird b1 = new Bird();
        b1.move();
        /*
            1.Animal和Ca之间有继承关系
            2.Animal是父类,Animal是子类
            3.Cat is a Animal
            父类型的引用允许指向子类型的对象。
            Animal a2 = new Cat();
            a2就是父类型的引用
            new Cat()就是一个子类型的对象
            允许a2这个父类型引用指向子类型的对象。

        */
        Animal a2 = new Cat();
        Animal a3 = new Bird();
        /*
            什么是多态? 多种状态
            Java程序分为编译阶段和运行阶段
            先来分析编译阶段:
                对于编译器来说,编译器只知道a2的类型是Animal
                所以编译器在编译的时候,会去Animal.class字节码文件中去找move方法,找到了绑定上move
                方法,编译成功,静态绑定成功
            分析运行阶段:
                运行阶段的时候,实际上在堆内存中存在的java对象是Cat对象,所以move的时候,真正参与move的对象是Cat
                所以运行阶段会动态执行Cat对象的move()方法,这个过程属于运行阶段的绑定,属于动态绑定

            多态表示多种形态:
                编译的时候一种形态
                运行的时候是一种形态。


         */
        a2.move();
        a3.move();
        //====================================================================
        Animal a5 = new Cat();
        //分析程序一定要分析编译阶段和运行阶段的动态绑定
        //若调用CatchMouse就必须进行强制向下转型
        //因为a5是Animal类型,转成Cat,Animal和Cat之间存在继承关系,所以才没有报错
        Cat x = (Cat)a5;
        x.CatchMouse();

        //向下转型有风险吗
        Animal a6 = new Bird();//表面上a6是一个Animal,运行的时候实际上是Brid
        //运行阶段,堆内存实际上创建的是Bird对象,在实际运行中,Bird 对象转换成Cat对象,
        //bird和Cat对象之间没有继承关系
        /*  Cat y = (Cat)a6;
            y.CatchMouse();*/
        //怎么避免:ClassCastException的产生
        /*
        新的内容,运算符:instanceof
            第一:instanceof可以在运行阶段动态判断引用指向的对象的类型
            第二:引用instanceof的语法:
                (引用instanceof类型)
            第三:instanceof运算符的运算结果只能是:true/fasle
            第四:c是一个引用,c变量保存了内存地址,指向了堆中的对象
                假设(c instanceof Cat)为True; 表示c内存中的java对象是一个Cat
                假设(c instanceof Cat)为false;表示c内存中的java对象不是一个Cat
                instanceof在运行阶段动态判断。

             任何时候任何地点,在对类型进行向下转型时,一定要使用instanceof运算符,可以很好的避免ClassCastException
         */
        System.out.println(a6 instanceof Cat);
        if(a6 instanceof Cat){//如果a6为Cat则进行强制类型转换
            Cat y = (Cat)a6;
            y.CatchMouse();
        }



    }
}
class Animal{
    public void move(){
        System.out.println("moving");
    }
}
class Cat extends Animal{
    @Override
    public void move() {
        System.out.println("Cat is Moving");
    }
    //除了move之外,应该有自己特有的行为
    public void CatchMouse(){
        System.out.println("catching");
    }
}
class Bird extends Animal{
    @Override
    public void move() {
        System.out.println("Bird is flying");
    }
    public void Sing(){
        System.out.println("bird is singing");
    }
}
package duotai;
/*
    程序员可以观察到底层,到底是Bird和Cat

    进行instanceof判断是因为:在以后的开发中,程序员可能看不到
 */
public class Test02 {
    public static void main(String[] args){
        Animal x = new Bird();
        Animal y = new Cat();
        if(x instanceof Bird){
            Bird a = (Bird)x;
            a.Sing();
        }else if(x instanceof Cat){
            Cat a = (Cat)x;
            a.CatchMouse();
        }
        if(y instanceof Cat){
            Cat b = (Cat)y;
            b.CatchMouse();
        }else if(y instanceof Bird){
            Bird b = (Bird)y;
            b.Sing();
        }

    }
}

package duotai;

public class Test03 {
    public static void main(String[] args){
        //main程序员A负责编写
        Atest a = new Atest();
        a.test(new Cat());
        a.test(new Bird());

    }
}
class Atest{
    //程序员B负责编写
    //这个test()方法的参数是一个Animal
    public void test(Animal a){
        //你写的这个方法别人会调用
        //别人调用的时候可能给你test()方法传过来一个Bird
        //当然可能识别的
        //对于我来说,不知道调用的时候,会传给我什么参数
        if(a instanceof Cat){
            Cat c = (Cat) a;
            c.CatchMouse();
        }else if(a instanceof Bird){
            Bird b = (Bird) a;
            b.Sing();
        }

    }
}
多态在实际应用中的作用

降低程序的耦合度,提高程序的扩展力

public class Master{
			public void feed(Dog d){}
			public void feed(Cat c){}
		}
		以上的代码中表示:MasterDog以及Cat的关系很紧密(耦合度高)。导致扩展力很差。

public class Master{
			public void feed(Pet pet){
				pet.eat();
			}
		}
		以上的代表中表示:MasterDog以及Cat的关系就脱离了,Master关注的是Pet类。
		这样MasterDog以及Cat的耦合度就降低了,提高了软件的扩展性。

面向对象的三大特征:
封装、继承、多态

有了封装,有了这种整体的概念之后。
对象和对象之间产生了继承。
有了继承之后,才有了方法的覆盖和多态。

这里提到了一个软件开发原则:
七大原则最基本的原则:OCP(对扩展开放,对修改关闭)
目的是:降低程序耦合度,提高程序扩展力。
面向抽象编程,不建议面向具体编程。

package duotai;

public class Test04 {
    public static void main(String[] args){
        Master m = new Master();
        Dog d = new Dog();
        Cats c = new Cats();
        YingWu y = new YingWu();

        m.feed(d);
        m.feed(c);
        m.feed(y);

    }

}
class Master{
    public void feed(Pet p){
        //编译的时候,编译器发现p是一个pet类,会去Pet类中找eat()方法,结果找到了,编译器通过
        //运行的时候发现底层实际是Dog对象,就会自动调用Dog对象对应的eat方法上。
        p.eat();
    }

}
class Pet{
    //这个方法可以不给具体的实现
    public void eat(){

    }

}
class Dog extends Pet{
    public void eat(){
        System.out.println("dog eat");
    }
}
class Cats extends Pet{
    public void eat(){
        System.out.println("Cat eat");
    }

}
class YingWu extends Pet{
    public void eat(){
        System.out.println("YingWu eat");
    }

}

解释之前遗留的问题

私有方法无法覆盖。

方法覆盖只是针对于“实例方法”,“静态方法覆盖”没有意义。(这是因为方法覆盖通常和多态联合起来)

静态方法存在方法覆盖吗?

​ 多态自然就和对象有联系,而静态方法的执行不需要对象,
​ 所以一般情况下,我们会说静态方法不存在方法覆盖

总结两句话:
私有不能覆盖。
静态不谈覆盖。

在方法覆盖中,关于方法的返回值类型。

​ 什么条件满足之后,会构成方法的覆盖呢?
​ 1、发生具有继承关系的两个类之间。
​ 2、父类中的方法和子类重写之后的方法:
​ 具有相同的方法名、相同的形式参数列表、相同的返回值类型。

​ 学习了多态机制之后:
​ “相同的返回值类型”可以修改一下吗?
对于返回值类型是基本数据类型来说,必须一致。
​ 对于返回值类型是引用数据类型来说,重写之后返回值类型可以变的更小(但意义不大,实际开发中没人这样写。)。

super关键字

super是一个关键字,全部小写

super和this对比学

this:

​ this能出现在实例方法中和构造方法中
​ this语法是:“this.”、“this()”
​ this不能使用在静态方法中
​ this. 大部分情况下是可以省略的,在区分实例变量和局部变量的时候是不能省略的

​ this()只能出现在构造方法的第一行,通过当前的构造方法去调用“本类”中的其他方法,目的是:代码复用

super:

​ super能出现在实例方法中和构造方法中
​ super语法是:“super.”、“super()”
​ super不能使用在静态方法中
​ super. 大部分情况下是可以省略的,
​ super()只能出现在构造方法的第一行,通过当前的构造方法去调用“父类”中的其他方法,目的是:创建子类对象的时候先初始化父类型特征

super()

表示通过子类的构造方法调用父类的构造方法
模式现实世界中的这种场景:要想有儿子,需要现有父亲

super() 只能出现在构造方法第一行,通过当前的构造方法去调用“父类”中的构造方法,目的是:创建子类对象的时候,先初始化父类型特征。

重要的结论

当一个构造方法的第一行:
既没有this()又没有super()的话,默认会有一个super();
表示通过当前子类的构造方法调用父类的无参数构造方法
所以必须保证父类的无参数构造方法是存在的

this()和super()不能共存,他们只能是都能存在构造方法第一行

无论怎么折腾,父类的无参数构造方法一定会执行的

package Super;
/*
1. super是一个关键字,全部小写
2. super和this对比学
    this:
        this能出现在实例方法中和构造方法中
        this语法是:“this.”、“this()”
        this不能使用在静态方法中
        this. 大部分情况下是可以省略的,在区分实例变量和局部变量的时候是不能省略的

        this()只能出现在构造方法的第一行,通过当前的构造方法去调用“本类”中的其他方法,目的是:代码复用
    super:
        super能出现在实例方法中和构造方法中
        super语法是:“super.”、“super()”
        super不能使用在静态方法中
        super. 大部分情况下是可以省略的,
        super()只能出现在构造方法的第一行,通过当前的构造方法去调用“父类”中的其他方法,目的是:创建子类对象的时候先初始化父类型特征
3. super()
    表示通过子类的构造方法调用父类的构造方法
    模式现实世界中的这种场景:要想有儿子,需要现有父亲
4. 重要的结论
    当一个构造方法的第一行:
        既没有this()又没有super()的话,默认会有一个super();
        表示通过当前子类的构造方法调用父类的无参数构造方法
        所以必须保证父类的无参数构造方法是存在的
5. this()和super()不能共存,他们只能是都能存在构造方法第一行
6. 无论怎么折腾,父类的无参数构造方法一定会执行的
 */
public class Test01 {
    public static void main(String[] args){
        new B();
        /*
        A类的无参数构造方法实现
        B类的无参数构造方法实现
         */
    }
}
class A{
    int i;
    public A(int i) {
        System.out.println("A类的有参数构造方法实现");
    }

    public A() {
        System.out.println("A类的无参数构造方法实现");
    }
/*
    一个类如果没有手动提供任何构造方法,系统会默认提供一个无参构造方法
     */

}
class B extends A{

    public B() {
        //调用父类中有参数的构造方法
        //super();//自动有的
        /*
        A类的无参数构造方法实现
        B类的无参数构造方法实现
         */
        this("123");
        /*
        A类的无参数构造方法实现
        B类的有参数构造方法实现
        B类的无参数构造方法实现
         */
        System.out.println("B类的无参数构造方法实现");
    }
    public B(String a){
        //super(); 默认有的 先调用
        /*
        A类的无参数构造方法实现
        B类的有参数构造方法实现
        B类的无参数构造方法实现
         */
        System.out.println("B类的有参数构造方法实现");
    }
}
super和this的执行顺序
package Super;

public class Test02 {
    public static void main(String[] args){
        new E();
    }

}
class C{
    public C() {
        System.out.println("C 执行");
    }
}
class D extends C{
    public D() {
        System.out.println("D 执行");
    }
    public D(String name){
        System.out.println("D name");
    }
}
class E extends D {
    public E() {
        this("name");
        System.out.println("E 执行");
    }
    public E(String name) {
        this(name,20);
        System.out.println("E name 执行");

    }
    public E(String name,int i) {
        super(name);
        System.out.println("E name i 执行");
    }
}
//
C 执行
D name
E name i 执行
E name 执行
E 执行

super(实例参数)的使用时间

package Super;
/*
在恰当的时间使用super(实际参数列表):
 */
public class Test03 {
    public static void main(String[] args){
        CreditAccount ca1 = new CreditAccount();
        System.out.println(ca1.getActno() + "," + ca1.getBalance() + "," + ca1.getCredit());
        CreditAccount ca2 = new CreditAccount("a",123,111);
        System.out.println(ca2.getActno() + "," + ca2.getBalance() + "," + ca2.getCredit());
    }
}
class Account{
    /*
    私有的方法只能在本类中访问
     */
    private String actno; //账号
    private double balance; //余额

    public Account() {
    }

    public Account(String actno, double balance) {
        this.actno = actno;
        this.balance = balance;
    }

    public String getActno() {
        return actno;
    }

    public double getBalance() {
        return balance;
    }

    public void setActno(String actno) {
        this.actno = actno;
    }

    public void setBalance(double balance) {
        this.balance = balance;
    }
}
//其他类型的账户:信用卡账户
class CreditAccount extends Account{

    private double credit;

    public CreditAccount(String actno, double balance, double credit) {
        /*
        this.actno = actno;
        this.balance = balance;
        以上两行代码在恰当的位置,正好可以使用:super(actno)
        通过子类的构造方法调用父类的构造方法
         */
        super(actno,balance);
        this.credit = credit;

    }

    public CreditAccount() {
    }

    public void setCredit(double credit) {
        this.credit = credit;
    }


    public double getCredit() {
        return credit;
    }
}
null,0.0,0.0
a,123.0,111.0

super执行的内存图

注意:虽然调用构造方法,在构造方法执行的过程中还调用了一连串的父类的构造方法,父类构造方法又继续调用了它的父类构造方法,但是实际上对象只创建了一个

021-super的原理

“super(实参)”到底是干嘛的?

super(实参):初始化当前对象的父类型特征,并不是创建新对象,实际上对象只能创建出一个

super关键字代表什么?

​ super关键字代表的就是“当前对象”的那部分父类型特征

例子

子类型和父类型无同名属性
package Super;


public class Test04 {
    public static void main(String[] args){
        Vip v = new Vip("z");
        v.Shopping();


    }
}
class Cumstor{
    String name;

    public Cumstor() {
    }

    public Cumstor(String name) {
        this.name = name;
    }

    public void Shopping(){

    }
}
class Vip extends Cumstor{
    public Vip() {
    }

    public Vip(String name) {
        super(name);
    }
    public void Shopping(){
        System.out.println(this.name + "shopping");
        System.out.println(super.name + "shopping");
        System.out.println(name + "shopping");
    }
}
zshopping
zshopping
zshopping

022-SuperTest04

子类型和父类型有同名属性
package Super;
/*
 this.和super.在大部分的情况下都是可以省略的
 this.在区分局部变量和实例变量的时候不可以省略
 super.父类中这个属性,子类中也有这个属性,想在子类型中访问父类型中的属性,就不可以省略
 */
public class Test05 {
    public static void main(String[] args){
        Vip v = new Vip("z");
        v.Shopping();


    }
}

class Cumstor{
    String name;

    public Cumstor() {
    }

    public Cumstor(String name) {
        this.name = name;
    }

    public void Shopping(){

    }
}
class Vip extends Cumstor{
    //假设子类也有一个同名属性
    String name;
    public Vip() {
    }

    public Vip(String name) {
        super(name);
    }
    //super和this都不能再静态方法中出现
    public void Shopping(){
        //this表示当前空间
        System.out.println(this.name + "shopping");
        //super表示当前对象的父类型特征(super是this指向的那个对象中的一块空间)
        System.out.println(super.name + "shopping");
        System.out.println(name + "shopping");
    }
}
//
nullshopping
zshopping
nullshopping

023-SuperTest05

super和this都不能再静态方法中出现

java如何区分子类属性和父类型属性

this.和super.在大部分的情况下都是可以省略的
this.在区分局部变量和实例变量的时候不可以省略
super.父类中这个属性,子类中也有这个属性,想在子类型中访问父类型中的属性,就不可以省略

super不能单独使用

super不是引用,super不保存内存地址,super也不指向任何对象
super只是代表当前对象内部的父类型特征

package Super;
/*
    通过测试得出的结论。super不是引用,super不保存内存地址,super也不指向任何对象
    super只是代表当前对象内部的父类型特征
 */
public class Test06 {
    public void doSome(){
        //输出引用的话,会自动调用引用的toSting方法
        System.out.println(this);
        //System.out.println(this.toString());
        //错误需要'.'
        System.out.println(super.);
    }
    public static void main(String[] args){
        Test06 t = new Test06();
        t.doSome();

    }
}

package Super;

public class Test07 {
    public static void main(String[] args){
        Cat c = new Cat();
        c.M();
    }
}
class Animal{
    public void move(){
        System.out.println("Animal move");
    }
}
class Cat extends  Animal{
    public void move(){
        System.out.println("Cat move");
    }

    public void M(){
        this.move();
        move();
        super.move();
    }
}
  • super.属性 - 访问父类的属性
  • super.方法名(实参) - 访问父类的方法
  • super.(实参) - 访问父类的构造方法

super执行的内存图

注意:虽然调用构造方法,在构造方法执行的过程中还调用了一连串的父类的构造方法,父类构造方法又继续调用了它的父类构造方法,但是实际上对象只创建了一个

[外链图片转存中…(img-L7U7fMuh-1634040754692)]

“super(实参)”到底是干嘛的?

super(实参):初始化当前对象的父类型特征,并不是创建新对象,实际上对象只能创建出一个

super关键字代表什么?

​ super关键字代表的就是“当前对象”的那部分父类型特征

例子

子类型和父类型无同名属性
package Super;


public class Test04 {
    public static void main(String[] args){
        Vip v = new Vip("z");
        v.Shopping();


    }
}
class Cumstor{
    String name;

    public Cumstor() {
    }

    public Cumstor(String name) {
        this.name = name;
    }

    public void Shopping(){

    }
}
class Vip extends Cumstor{
    public Vip() {
    }

    public Vip(String name) {
        super(name);
    }
    public void Shopping(){
        System.out.println(this.name + "shopping");
        System.out.println(super.name + "shopping");
        System.out.println(name + "shopping");
    }
}
zshopping
zshopping
zshopping

[外链图片转存中…(img-0aZuJNxt-1634040754692)]

子类型和父类型有同名属性
package Super;
/*
 this.和super.在大部分的情况下都是可以省略的
 this.在区分局部变量和实例变量的时候不可以省略
 super.父类中这个属性,子类中也有这个属性,想在子类型中访问父类型中的属性,就不可以省略
 */
public class Test05 {
    public static void main(String[] args){
        Vip v = new Vip("z");
        v.Shopping();


    }
}

class Cumstor{
    String name;

    public Cumstor() {
    }

    public Cumstor(String name) {
        this.name = name;
    }

    public void Shopping(){

    }
}
class Vip extends Cumstor{
    //假设子类也有一个同名属性
    String name;
    public Vip() {
    }

    public Vip(String name) {
        super(name);
    }
    //super和this都不能再静态方法中出现
    public void Shopping(){
        //this表示当前空间
        System.out.println(this.name + "shopping");
        //super表示当前对象的父类型特征(super是this指向的那个对象中的一块空间)
        System.out.println(super.name + "shopping");
        System.out.println(name + "shopping");
    }
}
//
nullshopping
zshopping
nullshopping

[外链图片转存中…(img-LhaJ8N99-1634040754693)]

super和this都不能再静态方法中出现

java如何区分子类属性和父类型属性

this.和super.在大部分的情况下都是可以省略的
this.在区分局部变量和实例变量的时候不可以省略
super.父类中这个属性,子类中也有这个属性,想在子类型中访问父类型中的属性,就不可以省略

super不能单独使用

super不是引用,super不保存内存地址,super也不指向任何对象
super只是代表当前对象内部的父类型特征

package Super;
/*
    通过测试得出的结论。super不是引用,super不保存内存地址,super也不指向任何对象
    super只是代表当前对象内部的父类型特征
 */
public class Test06 {
    public void doSome(){
        //输出引用的话,会自动调用引用的toSting方法
        System.out.println(this);
        //System.out.println(this.toString());
        //错误需要'.'
        System.out.println(super.);
    }
    public static void main(String[] args){
        Test06 t = new Test06();
        t.doSome();

    }
}

package Super;

public class Test07 {
    public static void main(String[] args){
        Cat c = new Cat();
        c.M();
    }
}
class Animal{
    public void move(){
        System.out.println("Animal move");
    }
}
class Cat extends  Animal{
    public void move(){
        System.out.println("Cat move");
    }

    public void M(){
        this.move();
        move();
        super.move();
    }
}
  • super.属性 - 访问父类的属性
  • super.方法名(实参) - 访问父类的方法
  • super.(实参) - 访问父类的构造方法
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小七rrrrr

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值