本章内容:

1.类(class)与实例(instance)
2.方法
3.this变量
4.构造方法
5.方法重载Overload
6.继承
7.多态
8.抽象类
9.接口
10.package包
11.作用域

1.类(class)与实例(instance)

现实世界中,我们定义了“人”这种抽象概念,而具体的人则是“小明”、“小红”、“小军”等一个个具体的人。所以,“人”可以定义为一个类(class),而具体的人则是实例(instance)
instance是对象实例,instance是根据class创建的实例,可以创建多个instance,每个instance类型相同,但各自属性可能不相同。
在Java中,我们来写一个完整的程序:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
// class的public前缀有且仅有一个
public class OOP {
// 在主类中定义一个demo方法
public static int demo(int x){
return x;
}
// 定义程序入口方法:main方法
public static void main(String[] args) {
// 调用定义好的demo方法
System.out.println(demo(5));
// 创建一个ming实例变量
Person ming = new Person();
ming.setBirth(2008);
ming.tel = "15867119504";
System.out.println(ming.getTel());
System.out.println(ming.getAge());
}
}

// 创建一个People类(类里包含属性和方法)
class Person {
public String tel;
private String name;
private int birth;

public void setBirth(int birth) {
this.birth = birth;
}

public String getTel(){
return this.tel;
}

// 这里说明一下:利用public型的getAge()方法调用private型的calcAge()方法
public int getAge() {
return calcAge(2019); // 调用private方法
}

// private方法:
private int calcAge(int currentYear) {
return currentYear - this.birth;
}
}

小结:

  • public是用来修饰字段的,它表示这个字段可以被外部访问。
  • private方法不允许外部调用,内部方法(即处于同一class中)是可以调用private方法

2.方法

1
2
3
4
修饰符 方法返回类型 方法名(方法参数列表) {
若干方法语句;
return 方法返回值;
}

方法返回值通过return语句实现,如果没有返回值,返回类型设置为void,可以省略return

2020-3-26 补充:
最近看到很多面试题,整理到值传递、引用传递,到网上搜了一圈,争议很大。带着一脸蒙圈,线上线下查了好多资料。最终有所收获,所以分享给大家,希望能对你有所帮助。
首先说下我的感受,这个题目出的很好,但是在 Java 中这个题目是有问题的(在下面我会解释)。并且,有很多结论是 Java 中只有 值传递。我认为这样说不够严谨。当然如果针对 Java 语言本身来讲,Java 中只有 值传递,没有引用传递,是正确的。但是如果针对 值传递,引用传递的定义来说,Java 中还是有引用传递

方法的参数有实参和形参之分,我们现在深入思考一下,方法中可以传递什么类型的值

  • 基本数据类型:整型,字符型,浮点型,布尔型

    1
    public static void test(int a, char b, double c, float d, boolean e){}
  • 引用数据类型:字符串,接口

    1
    public static void test(String a){}
  • 数组类型传递:

    1
    public static void test(int[] a, char[] b, double[] c,...){}
  • class类类型传递:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    public static void test(employee a,employee b){}
    ...
    class employee{
    private int id;

    public int getId() {
    return id;
    }
    public void setId(int id) {
    this.id = id;
    }
    }

总结:

  1. 基本数据类型、引用类型定义
            基本数据类:Java 中有八种基本数据类型“byte、short、int、long、float、double、char、boolean”
            引用类型:new 创建的实体类、对象、及数组
  2. 基本数据类型、引用类型在内存中的存储方式
            基本数据类型:存放在栈内存中。用完就消失。
            引用类型:在栈内存中存放引用堆内存的地址,在堆内存中存储类、对象、数组等。当没用引用指向堆内存中的类、对象、数组时,由 GC回收机制不定期自动清理。

3.this变量

在方法内部,可以使用一个隐含的变量this,它始终指向当前实例。因此,通过this.field就可以访问当前实例的字段。

如果没有命名冲突,可以省略this。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class Person {
private String name;

public String getName() {
return name; // 相当于this.name
}
}
但是,如果有局部变量和字段重名,那么局部变量优先级更高,就必须加上this:

class Person {
private String name;

public void setName(String name) {
this.name = name; // 前面的this不可少,少了就变成局部变量name了
}
}

4.构造方法

构造方法是专门用来创建对象的方法,通过关键字new来创建对象,是就是在调用构造方法
格式:public 类名称(参数类型 变量名称){方法体}
注意事项:

  • 构造方法的名称必须和所在的类名称完全一样,就连大小写也一样
  • 构造方法不要写返回值类型,连void都不要写
  • 构造方法不能return一个具体的返回值
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
public class OOP {
public static void main(String[] args) {
// 在通过new操作符调用的时候,构造方法的参数数量、位置和类型一一对应
Person p = new Person("chd",18,10086);
System.out.println(p.getAge());
// 调用实例对象的属性
System.out.println(p.tel);
System.out.println(p.getAge());
System.out.println(p.getName());
}
}

class Person {
// 这个不能省略,用以声明变量
private String name;
private int age;
public int tel;

// 这就是Java的构造方法,类似于python的def __init__函数
public Person(String name, int age,int tel) {
this.name = name;
this.age = age;
this.tel = tel;
}

public String getName() {
return this.name;
}

public int getAge() {
return this.age;
}
}

拓:多构造方法
可以定义多个构造方法,在通过new操作符调用的时候,编译器通过构造方法的参数数量、位置和类型自动区分:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class Person {
private String name;
private int age;

public Person(String name, int age) {
this.name = name;
this.age = age;
}

public Person(String name) {
this.name = name;
this.age = 12;
}

public Person() {
}
}

如果调用new Person("Xiao Ming", 20);,会自动匹配到构造方法public Person(String, int)

如果调用new Person("Xiao Ming");,会自动匹配到构造方法public Person(String)

如果调用new Person();,会自动匹配到构造方法public Person()

5.方法重载

这种方法名相同,但各自的参数不同(即参数数目不同,参数数据类型不同),称为方法重载(Overload)。注意:方法重载的返回值类型通常都是相同的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class Hello {
public void hello() {
System.out.println("Hello, world!");
}

public void hello(String name) {
System.out.println("Hello, " + name + "!");
}

public void hello(String name, int age) {
if (age < 18) {
System.out.println("Hi, " + name + "!");
} else {
System.out.println("Hello, " + name + "!");
}
}
}

6.继承

继承是面向对象编程中非常强大的一种机制,它首先可以复用代码。当我们让Student从Person继承时,Student就获得了Person的所有功能,我们只需要为Student编写新增的功能。
继承的特点:

  1. Java是单继承语言:一个类的直接父类只能有唯一一个
  2. Java语言可以多级继承:父类也可以继承父类的父类
  3. 一个父类可以拥有多个子类:兄弟子类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class Person {
private String name;
private int age;

public String getName() {...}
public void setName(String name) {...}
public int getAge() {...}
public void setAge(int age) {...}
}

class Student extends Person {
// 不要重复name和age字段/方法,
// 只需要定义新增score字段/方法:
private int score;

public int getScore() { … }
public void setScore(int score) { … }
}

super

super关键字表示父类(超类)。子类引用父类的字段时,可以用super.fieldName
实际上,这里使用super.name,或者this.name,或者name,效果都是一样的。编译器会自动定位到父类的name字段

1
2
3
4
5
class Student extends Person {
public String hello() {
return "Hello, " + super.name;
}
}

在继承时,如果父类存在构造函数,在这个时候,就必须使用super,其正确写法应为(牢记):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
public class OOP {
public static void main(String[] args) {
Person p = new Person("chd",18,10086);
// 实例s为Person的子类Student类的实例
Student s = new Student("gz",18,10010,100);
System.out.println(s.getAge());
System.out.println(p.getAge());
System.out.println(p.tel);
System.out.println(p.getAge());
System.out.println(p.getName());
System.out.println(s.grand);
}
}

class Person {
protected String name;
protected int age;
public int tel;

public Person(String name, int age,int tel) {
this.name = name;
this.age = age;
this.tel = tel;
}

public String getName() {
return this.name;
}

public int getAge() {
return this.age;
}
}

class Student extends Person{
protected int grand;

public Student(String name, int age, int tel,int grand) {
super(name, age, tel); // 自动调用父类的构造方法Person(name, age, tel)
this.grand = grand; // 这是新构建的字段
}

@Override // Override重写,方法名,参数,返回值类型完全相同
public int getAge(){
return this.grand;
}

}

继承小结:

  • Java只允许一个class继承自一个类,因此,一个类有且仅有一个父类。只有Object特殊,它没有父类
  • 子类无法访问父类的private字段或者private方法,为了让子类可以访问父类的字段,我们需要把private改为protected
  • 子类不会继承任何父类的构造方法。子类默认的构造方法是编译器自动生成的,不是继承的

7.多态

在继承关系中,子类如果与父类的方法名完全相同(方法名相同,参数相同,子类方法的返回值必须小于等于父类的返回值范围(推荐:子类和父类方法的返回值类型相同)),被称为重写(Override,这里要区别于Overload是方法名相同,但各自的参数不同)
举一个简单案例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class Main {
public static void main(String[] args) {
Person p = new Student();
p.run(); // 应该打印Person.run还是Student.run?
}
}

class Person {
// class中的方法不要写static
public void run() {
System.out.println("Person.run");
}
}

// Student类继承Person类
class Student extends Person {
@Override // 这是重写符号
// 将子类的run方法进行重写,增加新的功能
public void run() {
System.out.println("Student.run");
}
}

调用super关键字

在子类的覆写方法中,如果要调用父类的被覆写的方法,可以通过super来调用。例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Person {
protected String name;
public String hello() {
return "Hello, " + name;
}
}

class Student extends Person {
@Override
public String hello() {
// 调用父类的hello()方法:
return super.hello() + "!";
}
}

小结:
区分子类方法中变量名的三种访问方式:

  • 局部变量: 直接写成员变量名
  • 本类的成员变量: this.成员变量名
  • 父类的成员变量: super.成员变量名
1
2
3
4
5
6
7
8
9
class zi extends Fu{
public int num = 20; // 成员变量
public void method(){
int num = 10; // 局部变量
System.out.println(num); // 30:局部变量直接访问
System.out.println(this.num); // 20:本类的成员变量: this.成员变量名
System.out.println(super.num); // 10:父类的成员变量: super.成员变量名
}
}

final

继承可以允许子类覆写父类的方法。如果一个父类不允许子类对它的某个方法进行覆写,可以把该方法标记为final。用final修饰的方法不能被Override:

1
2
3
4
5
6
7
8
9
10
11
12
13
class Person {
protected String name;
public final String hello() {
return "Hello, " + name;
}
}

class Student extends Person {
// compile error: 不允许覆写
@Override
public String hello() {
}
}

2020-3-9 新增:
在父子类的继承关系中,如果成员变量重名,则创建子类访问对象时,访问有两种方法:

  1. 通过子类对象访问成员变量:等号左边时谁,就优先用谁,没有则向上找父类
  2. 通过成员方法访问成员变量:该方法属于谁,就有限用谁,没有向上找父类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
package com.Alibaba;

public class extendsField {
public static void main(String[] args) {
Fu fu = new Fu();
Zi zi = new Zi();// 实例化两个对象
/*
* 当父类和子类都具有num这个成员变量时
* 等号左边是谁,优先用谁
* */
System.out.println(zi.num); // 20
System.out.println("======");
zi.methodZi(); // 20:这个方法是子类的,优先使用子类的

zi.methodFu(); // 10:这个方法实在父类中定义的

}
}

// 创建父类
class Fu{
public int num = 10;
public int numFu = 100;
public void methodFu(){
System.out.println("这是父类方法");
// 优先使用本类中的num
System.out.println(num); // 10
}
}
// 创建子类
class Zi extends Fu{
public int num = 20;
public int numFu = 100;
public void methodZi(){
System.out.println("这是子类方法");
// 优先使用本类中的成员变量
System.out.println(num); // 20
}
}

8.抽象类

由于多态的存在,每个子类都可以覆写父类的方法,例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
class Person {
public void run() { … }
}

class Student extends Person {
@Override
public void run() { … }
}

class Teacher extends Person {
@Override
public void run() { … }
}

定义:
抽象类:抽象方法所在的类,必须是抽象类才行,在class前写上abstract即可
抽象方法:就是在方法强加上abstract关键字,然后去掉大括号,直接分号结束

使用方法:

  1. 不能直接创建new抽象类对象
  2. 必须用一个子类来继承抽象父类
  3. 子类必须覆盖重写抽象父类当中的所有抽象方法
  4. 创建子类实例对象进行使用
    如果一个class定义了方法,但没有具体执行代码,这个方法就是抽象方法,抽象方法用abstract修饰。因为无法执行抽象方法,因此这个类也必须申明为抽象类(abstract class)。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29

    public class OOP{
    public static void main(String[] args) {
    /*
    *这里值得值得注意:用abstract修饰的类就是抽象类。我们无法实例化一个抽象类
    */
    Person p = new Student(); // 第四点:创建子类实例对象
    p.age = 18;
    p.run();

    }
    }

    // 抽象类不仅要在类名前加abstract,也要在抽象方法名前加abstract
    // Person类用abstract修饰的,表示为抽象类,我们无法实例化抽象类
    abstract class Person{
    protected int age;
    public abstract void run();

    }

    // Student类继承Person类,并将父类的run方法进行重写
    class Student extends Person{
    @Override
    public void run(){
    System.out.println(super.age); // super指代父类
    System.out.println(this.age);
    }
    }
    小结:
  • 通过abstract定义的方法是抽象方法,它只有定义,没有实现。抽象方法定义了子类必须实现的接口规范;

  • 定义了抽象方法的class必须被定义为抽象类,从抽象类继承的子类必须实现抽象方法;

  • 抽象类无法被实例化(即className 变量名 = new className())

9.接口

含义

如果一个抽象类没有字段或者说没有属性,所有方法全部都是抽象方法【抽象方法:修饰符 abstract 返回值类型 方法名()】,就可以把该抽象类改写为接口:interface

1
2
3
4
5
6
// 抽象类
abstract class Person {
public abstract void run();
public abstract String getName();
public abstract String getName();
}

在Java中,可以使用interface可以声明一个接口:

1
2
3
4
interface Person{
void run();
String getName();
}

所谓interface,就是比抽象类还要抽象的纯抽象接口,因为它连字段都不能有。因为接口定义的所有方法默认都是public abstract的,所以这两个修饰符不需要写出来(写不写效果都一样)

实现方式

当一个具体的class去实现一个interface时,需要使用implements关键字

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class Student implements Person {
private String name;

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

@Override
public void run() {
System.out.println(this.name + " run");
}

@Override
public String getName() {
return this.name;
}
}

类实现多接口

在Java中,一个类只能继承自另一个类,不能从多个类继承。但是,一个类可以实现多个interface

1
2
3
class Student implements Person, Hello { // 实现了两个interface
...
}

接口继承

一个interface可以继承自另一个interface。interface继承自interface使用extends,它相当于扩展了接口的方法。例如:

1
2
3
4
5
6
7
8
interface Hello {
void hello();
}

interface Person extends Hello {
void run();
String getName();
}

此时,Person接口继承自Hello接口,因此,Person接口现在实际上有3个抽象方法签名,其中一个来自继承的Hello接口。

10.package包

1
2
3
4
5
6
7
8
9
10
程序目录结构:
package_sample
└─ src
├─ hong
│ └─ Person.java
│ ming
│ └─ Person.java
└─ mr
└─ jun
└─ Arrays.java

使用import导入相关包

  • 方式一:import 包名.某一类名
  • 方式二:import 包名.*

11.作用域

priavte 本类可见

public 所有类可见

protected 作用于继承关系,定义为protected的字段和方法可以被子类访问,以及子类的子类

default 本包可见(即默认的形式)(本包中的子类非子类均可访问,不同包中的类及子类均不能访问)

小结

  • Java内建的访问权限包括public、protected、private和package权限;

  • Java在方法内部定义的变量是局部变量,局部变量的作用域从变量声明开始,到一个块结束;

  • final修饰符不是访问权限,它可以修饰class、field和method;

  • 一个.java文件只能包含一个public类,但可以包含多个非public类。如果有public类,文件名必须和public类的名字相同。

    12.未完待续…


 评论

联系我 | Contact with me

Copyright © 2019-2020 谁知你知我,我知你知深。此恨经年深,比情度日久

博客内容遵循 署名-非商业性使用-相同方式共享 4.0 国际 (CC BY-NC-SA 4.0) 协议