zl程序教程

您现在的位置是:首页 >  Java

当前栏目

【JavaSE】知识点总结(3)

2023-04-18 14:25:46 时间

目录

一、类定义和使用

1. 类的定义

2. 类的实例化

3. 构造方法

构造方法的重载

 二、this关键字

三、 static 修饰属性

四、封装

2. getter与setter

五、继承

1. 继承的语法

2. 子类中访问父类

3. 关于继承原则

4. super关键字

5. super和this

6. protected 关键字

7. final 关键字

六、多态

1. 多态实现条件

2. 重写

3. 向上转移和向下转型

3.1 向上转型

3.2 向下转型

4. instanceof关键字


一、类定义和使用

类:描述一类对象所具备的共同属性和行为,比较抽象的概念,在类中只规定了一类对象所共同具备的属性(属性值不定)
对象:就是某个类的具体表现,在每个具体的对象中,属性才有了具体的值。

1. 类的定义

/ 创建类
class ClassName{
    一系列的属性;// 成员变量、实例变量、字段(属性)
    一系列的方法;// 成员方法、实例方法
}

class 为 定义类的关键字, ClassName 为类的名字, { } 中为类的主体。
类中包含的内容称为类的成员。属性主要是用来描述类的,称之为类的成员属性或者类成员变量。方法主要说明类具有哪些功能,称为类的成员方法。

public class称为主类
一个java源文件中只能有一个主类且必须和源文件的名称保持一致
在一个源文件中可以存在N个普通类 直接使用class定义 不加public。

【例如】

1. 定义一个狗类 

class Dog{
    publicStringname;//名字  
    publicStringcolor;//颜色
 
  public void bark(){
    System.out.printIn(name +"正在汪汪叫");
  
  public void wag(){
    System.out.println(name+"正在摇尾巴"); 
}

2. 定义一个学生类

public class Student{
    public String name;
    public String gender;
    public short age;
    public double score;
//上课
    public void DoClass(){}
//写作业
    public void DoHomework(){}  
//考试
    public void Exam(){}
}

【注意事项】

1. 一般一个文件当中只定义一个类

2. main方法所在的类一般要使用public修饰

3. public修饰的类必须要和文件名相同

2. 类的实例化

用类 类型创建对象的过程,称为类的实例化,在 java 中采用 new 关键字,配合类名来实例化对象。new关键字产生对象使用 " . " 来访问对象的属性和方法,同一个类(模板)可以产生多个实例对象。在程序中定义了一个类,相当于定义了一个新的类型,和String,Dog和Student一定义后也是两个独立的类型。
产生对象的语法:
类名称 对象引用名称 = new 类名称();

Dog d = new Dog();
Student s = new Student();

3. 构造方法

构造方法:类中特殊的成员方法
构造方法名称必须和类名称相同,在创建对象时,由编译器自动调用,在对象整个生命周期中只调用一次

在类中 定义构造方法的规则
1. 方法名称和类名称相同
2. 构造方法没有返回值的类型声明,不是void。

在Dog类中:

class Dog {
//构造方法
    public Dog() {
        System.out.println("Dog类定义时提供的构造方法");
    }
//不是构造方法
     public void Dog() {
        System.out.println("Dog类定义时提供的构造方法");
    }
}

 3. 当类中没有提供构造方法定义时,Java编译器在编译之后会生成默认的无参构造(不带参数的构造方法)。一旦类中提供了构造方法,则编译器就不再生成默认的无参构造。

  一旦在定义类时,提供了构造方法,则默认的无参构造就不再产生。

 构造方法的作用:在产生对象时,为对象的所有成员属性做初始化操作

在new Dog();这一行函数背后JVM干的事情:

1. 检测Dog类是否加载到JVM内存中,若没加载先把Dog类加载到内存中(类加载过程)
2. 只要有new就要在堆上产生新的空间,为Dog对象在堆上分配空间
3. 初始化对象分配的空间(此时用到构造方法进行属性的初始化)

Java中所有的数据类型都有默认值,这个默认值必须结合类来观察
只有在类中定义的成员变量才有默认值(构造方法)

当执行以下代码时,内存的变化过程:

        // 创建Dog类对象
        Dog six = new Dog();
        System.out.println(six.age);
        System.out.println(six.name);

 

由于构造方法在产生对象时,为所有成员变量赋值,因此可以在定义类时,给某些属性“就地初始化”特殊的默认值(不是数据类型的默认值,自己赋值) 

public class Date {
//    默认值改为2000
    public int year = 2000;
//    默认值改为1
    public int month = 1;
    public int day = 1;
 
    public static void main(String[] args) {
        Date date1 = new Date();
        System.out.println(date1.day);
        System.out.println(date1.month);
        System.out.println(date1.year);
    }
}

构造方法的重载

 二、this关键字

1.this修饰成员方法,表示直接从当前类中寻找同名方法

语法: this.方法名称(参数)

同一个类中没有继承关系其实可以省略this,代码在编译时会自行补上~ 

2.  this修饰构造方法
语法 : this(参数)表示调用构造方法

有三大要求:
(1).this调用构造方法只能在构造方法中使用,不能在成员方法中使用(构造方法是产生对象的)

 (2)this调用其他的构造方法必须在当前构造方法的首行

 (3)this调用必须是线性的,不能成环(就是不能你调用我,我又调用你,这样程序一直相互调用,会停不下来) 

三、 static 修饰属性

static 修饰属性,则该属性为静态变量,直接通过类名称来调用,存储在JVM的方法区,该类的所有对象共享此变量!类中的有些属性是类本身所具备的属性,和具体的某个对象无关类中的所有对象这个属性值都是相同的

public class Animal {
 
    public String name;
    public int age;
    public static String category;
 
    public Japanese(String name,int age,String category){
        this.name = name;
        this.age = age;
        this.category = category;
    }
 
    public static void main(String[] args) {
        Animal a1 = new Animal("小白",5,"animal");
        Animal a2 = new Animal("妞妞",6,"animal");
 
        a1.category= "plant";//显然不是,但是为了举例~
        System.out.println("姓名:" + a1.name + "  年龄:" + a1.age + "  属性:" + a1.category);
        System.out.println("姓名:" + a2.name + "  年龄:" + a2.age + "  属性:" + a2.category);
 
    }
}

【关于静态变量的说明】
1. static变量只存在于类之中,方法中千万不能定义静态变量

 2. 静态属性直接通过类名称来访问(推荐的),没有任何该类对象,这个属性也是存在的

3. 关于静态变量的存储在JVM的方法区,所有该类对象共享

四、封装

1.封装 : 可见范围的问题,体现的就是易用性与保护性。

1. private关键字实现属性的封装
private关键字:只在当前类的内部可见,出了类的{ },对外部完全隐藏。

public关键字:被public修饰的内容,在当前项目中都可见,描述公共的,公开的内容

public和private都称为访问修饰符,描述属性或方法的可见性

在下列代码中,CPU和memory这两个属性用privative修饰,只在当前类Computer中可见,外界是无法访问这俩个属性的。screen这个属性用public修饰,在当前项目都可见

class Computer {
    // 这个属性的可见性只在Computer类的内部可见,对类的外部完全隐藏!
    private String cpu;//CPU
    private String memory; // 内存
    public String screen; // 显示器
}

2. getter与setter

当属性被private封装(隐藏)之后,在类的外部要想使用这些属性,必须通过类对外提供的方法来使用
getter:在类的外部获取某些private属性值
setter: 在类的外部设置某些private属性值

BankCard类(银行卡类):

public class BankCard {
    // 卡号
    private String cardNum;
    // 密码
    private String password;
    // 余额
    private double balance;
 
    private Scanner scanner = new Scanner(System.in);
 
    public BankCard(String cardNum, String password) {
        this.cardNum = cardNum;
        this.password = password;
    }
 
    // 对于卡号来说,只需要取得值
    public String getCardNum() {
        System.out.println("请输入您的密码 : ");
        String pass = scanner.nextLine();
        if (this.password.equals(pass)) {
            System.out.println("密码正确,正在为您查询卡号");
            return this.cardNum;
        }
        return "密码错误,请查证后重新输入";
    }
    
    public String getPassword() {
        System.out.println("发送手机验证码 ....");
        // 匹配验证码
        return this.password;
    }
    public void setPassword() {
        System.out.println("请输入您之前的密码 : ");
        String pass = scanner.nextLine();
        if (this.password.equals(pass)) {
            System.out.println("请输入新密码 : ");
            pass = scanner.nextLine();
            this.password = pass;
            System.out.println("密码修改成功,请通过新密码登录");
        }else {
            System.out.println("密码错误,请查证后再输入");
        }
    }
}

Test类:

public class Test {
    public static void main(String[] args) {
        BankCard bankCard = new BankCard("13467","123456");
        String num = bankCard.getCardNum();
        System.out.println(num);
        String pass = bankCard.getPassword();
        System.out.println(pass);
        // 设置新密码
        bankCard.setPassword();
        System.out.println("获取银行卡号 : " + bankCard.getCardNum());
    }
}

类的外部要操作这些属性,必须使用类提供的方法来操作,就可以对使用者使用属性做一些规则和要求。若不使用private封装,则使用者就可以随心所欲的操作属性,无法管控 !

思考: private能否直接修饰一个类?

答:类定义出来为了创建对象,private修饰的内容只在当前类中可见。

假设 : private可以修饰类 这个类定义之后不见了(有矛盾!)

五、继承

继承主要解决的问题是:共性的抽取,实现代码复用。
简单来说就是允许在原有类的基础上进行拓展,实现新功能,这样新产生的类称为派生类/子类,原有类称为基类/父类。

1. 继承的语法

修饰符 class 子类 extends 父类 {
// ... 
}

例如:狗和猫都是动物,那么我们就可以将猫和狗以及其他动物共同属性及方法进行抽取放在动物Animal这个类中,采用继承的思想来达到共用。

 使用继承后对上述的猫和狗类设计:

// Animal.java
public class Animal{
    String name;
    int age;
    public void eat(){
        System.out.println(name + "正在吃饭");
    }
    public void sleep(){
        System.out.println(name + "正在睡觉");
    }
}
// Dog.java
public class Dog extends Animal{
    void bark(){
        System.out.println(name + "汪汪汪~~~");
    }
}
// Cat.Java
public class Cat extends Animal{
    void mew(){
        System.out.println(name + "喵喵喵~~~");
    }
}
// TestExtend.java
public class TestExtend {
    public static void main(String[] args) {
        Dog dog = new Dog();
// dog类中并没有定义任何成员变量,name和age属性肯定是从父类Animal中继承下来的
        System.out.println(dog.name);
        System.out.println(dog.age);
// dog访问的eat()和sleep()方法也是从Animal中继承下来的
        dog.eat();
        dog.sleep();
        dog.bark();
    }
}

【注意】

1. 子类会将父类中的成员变量或者成员方法继承到子类中了

2. 子类继承父类之后,必须要新添加自己特有的成员,体现出与基类的不同,否则就没有必要继承了

2. 子类中访问父类

(1)子类中访问父类的成员变量

public class Base {
    int a;
    int b;
    int c;
}
 
public class Derived extends Base{
    int a; // 与父类中成员a同名,且类型相同
    char b; // 与父类中成员b同名,但类型不同
    public void method(){
        a = 100; // 访问父类继承的a,还是子类自己新增的a?
        b = 101; // 访问父类继承的b,还是子类自己新增的b?
        c = 102; // 子类没有c,访问的肯定是从父类继承下来的c
        // d = 103; // 编译失败,因为父类和子类都没有定义成员变量b
    }
}

在子类方法中 或者 通过子类对象访问成员时:

(1)如果访问的成员变量子类中有,优先访问自己的成员变量。

(2)如果访问的成员变量子类中无,则访问父类继承下来的,如果父类也没有定义,则编译报错。

(3)如果访问的成员变量与父类中成员变量同名,则优先访问自己的。

成员变量访问遵循就近原则,自己有优先自己的,如果没有则向父类中找。

(2)子类中访问父类的成员方法

public class Base {
    public void methodA(){
        System.out.println("Base中的methodA()");
    }
    public void methodB(){
        System.out.println("Base中的methodB()");
    }
}
public class Derived extends Base{
    public void methodA(int a) {
        System.out.println("Derived中的method(int)方法");
    }
    public void methodB(){
        System.out.println("Derived中的methodB()方法");
    }
    public void methodC(){
        methodA(); // 没有传参,访问父类中的methodA()
        methodA(20); // 传递int参数,访问子类中的methodA(int)
        methodB(); // 直接访问,则永远访问到的都是子类中的methodB(),基类的无法访问到
    }
}

(1)通过子类对象访问父类与子类中不同名方法时,优先在子类中找,找到则访问,否则在父类中找,找到则访问,否则编译报错。

(2)通过派生类对象访问父类与子类同名方法时,如果父类和子类同名方法的参数列表不同(重载),根据调用方法适传递的参数选择合适的方法访问,如果没有则报错。

3. 关于继承原则

(1)关于构造方法的说明

关于构造方法的说明:调用子类构造方法产生子类对象之前,默认调用父类的构造方法先产生父类对象,而后产生子类对象。

比如:

public class Parent {
    public Parent(){
        System.out.println("Parent的无参构造");
    }
}
public class Child extends Parent{
    public Child(){
        System.out.println("Child的无参构造");
    }
}
public class Test {
   public static void main(String[] args) {
        Child c = new Child();//由于Child继承父类Parent,先生成调用父类无参构造,再调用子类无参构造
    }
}

(2)Java中的单继承局限

Java中一个类只能使用extends继承一个父类,不能同时继承多个父类 =》 多重继承。但是Java允许多层继承,例如狗是一种动物,哈士奇是狗的一种,因此狗继承动物类,哈士奇继承狗类。此时哈士奇这个类就具备了狗类和动物。

4. super关键字

4.1 super修饰属性

super修饰属性,表示直接从父类中寻找同名变量

子类和父类中可能会存在相同名称的成员,如果要在子类方法中访问父类同名成员时,该如何操作?直接访问是无法做到的,Java提供了super关键字,该关键字主要作用:在子类方法中访问父类的成员

public class Child extends Parent{
    String name = "儿子";
    public void fun(){
        System.out.println("Child中fun()方法");
        此时若父类子类中出现同名变量,则优先调用子类中的变量
        System.out.println(name);//子类中的name(就近匹配原则)
        使用super.变量名 -> 直接从父类中取该变量
        System.out.println(super.name);//父类中的name
    }
}

4.2 super修饰构造方法

super调用父类的构造方法 语法: super(参数); // 表示调用类的对应构造方法

(1)子类构造方法的首行必须使用super(参数);表示先调用父类的构造方法。当父类存在无参构造时,可以省略该语句;

(2)若父类中不存在无参构造,则必须在子类构造方法的首行显示调用父类的有参构造

(3)若父类中不存在无参构造,则supe参数必须写在子类构造方法首行,因此此时在子类中不存在子类构造方法之间的相互调用(this语句)

原因:(this(参数也得写在构造方法首行)=>矛盾!

4.3 super修饰普通方法

语法: super.方法名称(参数);直接从父类中调用同名方法

public class Parent {
     public void fun(){
        System.out.println("Parent中的fun()方法");
    }
}
public class Child extends Parent{
    public void fun(){
        System.out.println("Child中fun()方法");
    }
    public void test(){
//        当父类和子类都存在fun方法时,按照就近匹配原则,会调用子类中的fun方法
        fun();
//        因此,若需要调用父类的fun方法,需要使用super关键字
        super.fun();
    }

5. super和this

super和this都可以在成员方法中用来访问:成员变量和调用其他的成员函数,都可以作为构造方法的第一条语句,那他们之间有什么区别呢?

【相同点】

1. 都是Java中的关键字

2. 只能在类的非静态方法中使用,用来访问非静态成员方法和字段

3. 在构造方法中调用时,必须是构造方法中的第一条语句,并且不能同时存在

【不同点】

1. this是当前对象的引用,当前对象即调用实例方法的对象,super相当于是子类对象中从父类继承下来部分成员的引用 

2. 在非静态成员方法中,this用来访问本类的方法和属性,super用来访问父类继承下来的方法和属性

3. 在构造方法中:this(...)用于调用本类构造方法,super(...)用于调用父类构造方法,两种调用不能同时在构造方法中出现

4. 构造方法中一定会存在super(...)的调用,用户没有写编译器也会增加,但是this(...)用户不写则没有

this表示当前对象的成员变量或方法的调用
super表示父类对象的成语变量与方法的调用

6. protected 关键字

在类和对象章节中,为了实现封装特性,Java中引入了访问限定符,主要限定:类或者类中成员能否在类外或者其他包中被访问。

private(当前类的内部可见)< default(当前包(同级目录)的不同类之间可见)< protected

default表示不做修饰

(1)在有继承关系的类之间,子类可以直接访问父类中protected修饰的属性或方法

 

(2)当一个属性在类中被protected修饰符修饰,这个属性在同一个包中的不同类(没有继承关系的)仍然可见! 

 

7. final 关键字

final关键可以用来修饰变量、成员方法以及类。

(1)修饰变量或字段,表示常量(即不能修改)

final int a = 10;
a = 20; // 编译出错

(2)修饰类:表示此类不能被继承

final public class Animal {
...
}
public class Bird extends Animal {
...
}
// 编译出错

(3)修饰方法:表示该方法不能被重写

六、多态

多态的概念:简而言之“相同的行为/方法,通过不同对象来使用时,展现出不同的状态,这样的一种特性称为多态性

1. 多态实现条件

在java中要实现多态,必须要满足如下几个条件,缺一不可:

1. 必须在继承体系下

2. 子类必须要对父类中方法进行重写

3. 通过父类的引用调用重写的方法

多态体现:在代码运行时,当传递不同类对象时,会调用对应类中的方法。

 

2. 重写

重写(override):重写是子类对父类非静态、非private修饰,非final修饰,非构造方法等的实现过程进行重新编写, 返回值和形参都不能改变(方法权限可以不同)。即外壳不变,核心重写!重写的好处在于子类可以根据需要,定义特定于自己的行为。 也就是说子类能够根据需要实现父类的方法。

【方法重写的规则】

(1)子类在重写父类的方法时,一般必须与父类方法原型一致: 返回值类型 方法名 (参数列表) 要完全一致

(2)被重写的方法返回值类型可以不同,但是必须是具有父子关系的

(3)访问权限不能比父类中被重写的方法的访问权限更低。

(4)父类被static、private修饰的方法、构造方法都不能被重写。

(5)重写的方法, 可以使用 @Override 注解来显式指定. 

重写和重载的区别

方法重载是一个类的多态性表现,而方法重写是子类与父类的一种多态性表现

 

静态绑定:也称为前期绑定(早绑定),即在编译时,根据用户所传递实参类型就确定了具体调用那个方法。典型代表函数重载。

动态绑定:也称为后期绑定(晚绑定),即在编译时,不能确定方法的行为,需要等到程序运行时,才能够确定具体调用那个类的方法。

3. 向上转移和向下转型

3.1 向上转型

向上转型:实际就是创建一个子类对象,将其当成父类对象来使用。

语法格式:父类类型 对象名 = new 子类类型();

//animal是父类类型,但可以引用一个子类对象,因为是从小范围向大范围的转换。
Animal animal = new Cat("元宝",6);

父类名称 父类引用 = new 子类实例();

通过这个父类引用调用方法时,到底调用的是谁?

答:看这个new在哪里,只要new的子类重写了调用的方法,则调用的一定是这个子类重写后的方法,只有当子类没有进行该方法重写时,才会调用父类中的方法。

3.2 向下转型

Animal animal = new Dog();
animal.方法名称();
若Dog类中有一个独有的方法bite0); // 只有Dog类中有
此时能否通过父类的引用animal.bite();?

此时若需要调用Dog类中独有的bite方法,就需要脱掉Animal外衣,还原为Dog类的引用才可以,这种操作就称为向下转型.

将一个子类对象经过向上转型之后当成父类方法使用,再无法调用子类的方法,但有时候可能需要调用子类特有的方法,此时:将父类引用再还原为子类对象即可,即向下转换。

语法 : 子类名称 子类实例 =(子类名称)类引用;

 向下转型由于存在类型的强转,有可能会出现类型转换异常.

 (1)向下转型必须发生在有继承关系的类之间,毫无关系的类,对象不能发生向下转型(强转)

public static void main(String[] args) {
        Animal animal = new Dog();
        Cat cat = (Cat) animal;
        cat.eat();
    }

(2)要发生向下转型,必须先发生向上转型 

4. instanceof关键字

 由于向下转型存在类型转换异常的风险,引入了instanceof关键字

用法:引用名称 instanceof 类名称 => 返回布尔值
true: 表示当前用确实指向对应类的实例,可以强转
false: 表示当前引用与对应类的实例毫无关系,不能强转

一般使用该关键字搭配分支语句来进行向下转型:

if(animal instanceof Dog) {
    Dog dog = (Dog) animal;
}

什么时候会用到向下转型
通过相同的父类引用接收了子类实例之后fun(Animal animal){
// 需要在fun方法中,调用某个具体子类独有的方法/属性,需要用到向下转型

}