面向对象 | 我的日常分享

面向对象

面向对象

一、类与对象

1.现实中

  • 对象:对象是类的一个实例(对象不是找个女朋友),有状态和行为。例如,一条狗是一个对象,它的状态有:颜色、名字、品种;行为有:摇尾巴、叫、吃等。
  • :类是一个模板,它描述一类对象的行为和状态。

下图中男孩(boy)女孩(girl)类(class),而具体的每个人为该类的对象(object)

img

下图中汽车类(class),而具体的每辆车为该汽车类的对象(object),对象包含了汽车的颜色、品牌、名称等。

img

2.Java中

类可以看成是创建 Java 对象的模板。

一个类可以包含以下类型变量:

  • 局部变量:在方法、构造方法或者语句块中定义的变量被称为局部变量。变量声明和初始化都是在方法中,方法结束后,变量就会自动销毁。
  • 成员变量:成员变量是定义在类中,方法体之外的变量。这种变量在创建对象的时候实例化。成员变量可以被类中方法、构造方法和特定类的语句块访问。
  • 类变量:类变量也声明在类中,方法体之外,但必须声明为 static 类型。

一个类可以拥有多个方法,在上面的例子中:eat()、run()、sleep() 和 name() 都是 Dog 类的方法。

语法:

1
2
3
4
5
6
7
8
9
public class 类名{
//属性1
//属性2
//属性n

//方法1
//方法2
//方法n
}

注意:类中的属性不再书写static,后续详解。

案例1:类的定义

1
2
3
4
5
6
7
8
9
10
11
12
13
public class Dog{
String breed;//品种
int age;//年龄
String sex;//性别
String furColor;//毛色

public void eat(){
System.out.println("eat...");
}
public void sleep(){
System.out.println("sleeping...");
}
}

案例2:对象的创建

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class TestCreateObject{
public static void main(String[] args){
Dog myDog = new Dog();

//访问属性
myDog.breed = "萨摩";
myDog.age = 2;
myDog.sex = "公";
myDog.furColor = "白色";

System.out.println(myDog.breed + " " + myDog.age + " " + myDog.sex + " " + myDog.furColor);;

//调用方法
myDog.eat();
myDog.sleep();
}
}

运行结果:

image-20220124151831811

3.类与对象的关系

  • 类:定义了对象应具有的特征和行为,类是对象的模板。
  • 对象:拥有多个特征和行为的实体,对象是类的实例。

二、实例变量

  • 思考:之前的局部变量,要求必须赋值后在使用,否则编译错误,对于实例变量而言,未赋值能直接访问吗?
1
2
3
4
5
6
public class TestCreateObject{
public static void main(String[] args){
Dog myDog = new Dog();
System.out.println(myDog.breed + " " + myDog.age + " " + myDog.sex + " " + myDog.furColor);;
}
}

运行结果:

可以直接访问,具有默认值。

image-20220124152700385

实例变量的默认值:

1
2
3
4
5
整数:0
小数:0.0
字符:\u0000 空格
布尔:false
其他:null

实例变量与局部变量的区别

局部变量 实例变量
定义位置 方法或方法内的结构中 类的内部,方法的外部
默认值 无默认值 有默认值
使用范围 从定义行到包含其结构结束 本类有效
命名冲突 不允许与局部变量重名 不允许与实例变量重名,可与局部变量重名,局部变量优先。

三、实例方法

  • 对象的实例方法包含两部分:方法的声明和方法的实现。
  • 方法的声明:
    1. 代表对象能做什么
    2. 组成:修饰符 返回值类型 方法名(形参列表)
  • 方法的实现:
    1. 代表对象怎么做:即如何实现对应功能
    2. 组成:{ 逻辑代码 }

上面中定义的Dog类中的eat是实例方法。

1
2
3
4
5
6
public class Dog{
public void eat(){
System.out.println("eat...");
}
}

案例1:

定义学生类,包含属性姓名(name)、年龄(age)、性别(sex)、分数(score);包含方法打招呼(sayHi),打印学生所有信息。并创建多个学生对象,为其各个属性赋值,并调用方法。

Student.java

1
2
3
4
5
6
7
8
9
10
public class Student {
String name;
int age;
String sex;
double score;

public void sayHi(){
System.out.println("大家好,我是"+name+" 年龄"+age+" 性别"+sex+" 分数"+score);
}
}

TestStudent.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public class TestStudent {
public static void main(String[] args) {
//创建学生对象
Student xiaoming = new Student();
//属性赋值
xiaoming.name = "小明";
xiaoming.age = 18;
xiaoming.sex = "男";
xiaoming.score = 98;
//调用方法
xiaoming.sayHi();

//创建学生对象
Student xiaoli = new Student();
//属性赋值
xiaoli.name = "小李";
xiaoli.age = 20;
xiaoli.sex = "男";
xiaoli.score = 95;
//调用方法
xiaoli.sayHi();
}
}

运行结果:

image-20220130204426168

案例2:

创建一个老师类Teacher,包含属性姓名(name)、年龄(age)、工资(salary),方法打招呼sayHi,认识学生know。

Teacher.java

1
2
3
4
5
6
7
8
9
10
11
12
public class Teacher {
String name;
int age;
double salary;

public void sayHi(){
System.out.println("大家好!我是"+name+"老师,今年"+age+"岁。");
}
public void know(Student stu){
System.out.println("认识了学生"+stu.name);
}
}

TestTeacher.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class TestTeacher {
public static void main(String[] args) {
//创建老师对象
Teacher wang = new Teacher();
//属性赋值
wang.name = "王老师";
wang.age = 32;
wang.salary = 30000;

//创建学生对象
Student xiaoming = new Student();
//属性赋值
xiaoming.name = "小明";
xiaoming.age = 18;
xiaoming.sex = "男";
xiaoming.score = 98;

//调用老师方法
wang.sayHi();
wang.know(xiaoming);
}
}

运行结果:

image-20220203135016973

四、方法重载

有些情况下,对象的同一种行为可能存在多种实现过程,例如,人的”吃“行为,吃饭和吃药都是吃,但是吃的物质和过程存在差异,所以到底采用哪种实现过程取决于调用者给定的参数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class Person{
public void eat(食物 a){
//食物放入口中
//咀嚼
//咽下
}
public void eat(药物 b){
//药物放入口中
//喝水
//咽下
}
public void eat(口香糖 c){
//口香糖放入口中
//咀嚼
//吐出
}
}
  • 重载(overload):一个类定义多个相同名称的方法。
  • 要求:
    1. 方法名称相同
    2. 参数列表不同(类型、个数、顺序)
    3. 与访问修饰符、返回值类型无关
  • 调用带有重载的方法时,需要根据传入的实参去找到与之匹配的方法。
  • 好处:灵活、方便、屏蔽使用差异

案例1:

Operation类中定义重载方法。

Operation.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class Operation {
public void show(){
System.out.println("这是一个无参的方法");
}
public void show(int a){
System.out.println("这是一个带int参数的方法");
}
public void show(String s){
System.out.println("这是一个带String参数的方法");
}
public void show(int a,String s){
System.out.println("这是一个带int String参数的方法");
}
public void show(String s,int a){
System.out.println("这是一个带String int参数的方法");
}
}

TestOperation.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class TestOperation {
public static void main(String[] args) {
Operation oper = new Operation();
//调用无参show方法
oper.show();
//调用带一个int参数的方法
oper.show(100);
//调用带一个String参数的方法
oper.show("hello");
//调用带一个int和一个String参数的方法
oper.show(200,"hello");
//调用带一个String和一个int参数的方法
oper.show("hello",300);
}
}

运行结果:

image-20220203143009028

思考:以下方法是不是重载?

1
2
public void m(int a){}
public void m(int b){}

上面两个方法的方法名称和参数列表都相同,只有参数名称不一样,不满足方法重载的条件,编译报错。

image-20220203143407285

image-20220203143345326

注意:只是参数名称不同,并不能构成方法的重载。

五、构造方法

类中的特殊方法,主要用于创建对象。

  • 特点:
    1. 名称与类名完全相同
    2. 没有返回值类型
    3. 创建对象时,触发构造方法的调用,不能通过手动句点调用
  • 注意:如果没有在类中手动定义任何构造方法,编译器则默认提供无参构造方法

案例1:

Student.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class Student {
String name;
int age;
String sex;
double score;
//无参构造方法
public Student(){
System.out.println("无参构造方法");
name = "hhhh";
age = 18;
sex = "男";
}
public void sayHi(){
System.out.println("大家好,我是"+name+" 年龄"+age+" 性别"+sex+" 分数"+score);
}
}

TestStructure.java

1
2
3
4
5
6
7
public class TestStructure {
public static void main(String[] args) {
//new Student()中未传入任何参数,将调用无参构造方法
Student stu = new Student();
stu.sayHi();
}
}

运行结果:

创建对象的时候,调用无参构造方法,给对象属性赋值。而score未赋值,所以为0.0

image-20220203144815695

对象创建过程的内存分配,Student stu此时再堆中创建了一个Student类型的stu变量,这时变量没有值,当new Student()时,首先在栈中开辟了对象空间,为各个属性赋予初始值,然后在执行构造方法中的代码,然后将开辟的对象空间的引用给堆中的stu变量。

因为构造方法中没有给score赋值,所以score的默认值0.0

image-20220203150938793

构造方法的重载

构造方法也可重载,遵循重载规则,与普通方法类似。

案例2:

Student.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
public class Student {
String name;
int age;
String sex;
double score;
//无参构造方法
public Student(){
System.out.println("无参构造方法");
}
//带String int 的构造方法
public Student(String name1,int age1){
System.out.println("带String int参数的构造方法");
name = name1;
age = age1;
}
//带int String 的构造方法
public Student(int age1,String name1){
System.out.println("带int String参数的构造方法");
age = age1;
name = name1;
}
//带String int String的构造方法
public Student(String name1,int age1,String sex1){
System.out.println("带String int String参数的构造方法");
name = name1;
age = age1;
sex = sex1;
}
public void sayHi(){
System.out.println("大家好,我是"+name+" 年龄"+age+" 性别"+sex+" 分数"+score);
}
}

TestStructure.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class TestStructure {
public static void main(String[] args) {
//new Student()中未传入任何参数,将调用无参构造方法
Student stu = new Student();
stu.sayHi();
//调用String int参数构造方法
stu = new Student("xiaoming",18);
stu.sayHi();
//调用int String参数构造方法
stu = new Student(19,"xiaohong");
stu.sayHi();
//调用String int String参数构造方法
stu = new Student("zhangsan",22,"男");
stu.sayHi();
}
}

运行结果:

image-20220203165135201

默认构造方法

如果类中定义了构造方法,则编译器不提供默认的无参构造方法,如果此时没有定义无参构造方法,当调用时会报错。

案例3:

Student.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class Student {
String name;
int age;
String sex;
double score;
//无参构造方法
/*public Student(){
System.out.println("无参构造方法");
}*/
//带String int 的构造方法
public Student(String name1){
System.out.println("带String参数的构造方法");
name = name1;
}
public void sayHi(){
System.out.println("大家好,我是"+name+" 年龄"+age+" 性别"+sex+" 分数"+score);
}
}

TestStructure.java

1
2
3
4
5
6
7
8
9
10
public class TestStructure {
public static void main(String[] args) {
//正常
Student stu = new Student("xiaoming");
stu.sayHi();
//报错
Student stu1 = new Student();
stu1.sayHi();
}
}

运行结果:

image-20220203170024677

image-20220203170036958

六、this关键字

类是模板,可服务于此类的所有对象,this是类中的默认引用,代表当前实例;当类服务于某个对象时,this则指向这个对象。

案例1:this的第一种用法 ,调用实例属性,实例方法

调用实例属性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
//带String int String的构造方法
public Student(String name1,int age1,String sex1){
System.out.println("带String int String参数的构造方法");
name = name1;
age = age1;
sex = sex1;
}
//以上代码可改为
public Student(String name,int age,String sex){
System.out.println("带String int String参数的构造方法");
this.name = name;
this.age = age;
this.sex = sex;
}

调用实例方法

Student.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
public class Student {
String name;
int age;
String sex;
double score;
//无参构造方法
public Student(){
System.out.println("无参构造方法");
}
//带String int String的构造方法
public Student(String name,int age,String sex){
System.out.println("带String int String参数的构造方法");
this.name = name;
this.age = age;
this.sex = sex;
}
public void sayHi(){
System.out.println("大家好,我是"+name+" 年龄"+age+" 性别"+sex+" 分数"+score);
}
public void eat(){
System.out.println("吃");
this.sayHi();
}
}

TestThis.java

1
2
3
4
5
6
public class TestThis {
public static void main(String[] args) {
Student stu = new Student("xiaoming",18,"男");
stu.eat();
}
}

运行结果:

image-20220203173335583

案例2:this的第二种用法,调用本类中其他构造方法

在构造方法中,调用本类的其它构造方法,即可复用构造方法中的逻辑代码。

注意:通过this调用本类中其它构造方法,这条语句必须在构造方法的首行,且只能有一条。

Student.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
public class Student {
String name;
int age;
String sex;
double score;
//无参构造方法
public Student(){
System.out.println("无参构造方法");
}
//带String int String的构造方法
public Student(String name, int age, String sex){
System.out.println("带String int String参数的构造方法");
this.name = name;
this.age = age;
this.sex = sex;
}
//带String int String double的构造方法
public Student(String name, int age, String sex,double score){
this(name,age,sex);
System.out.println("带String int String double参数的构造方法");
this.score = score;
}
public void sayHi(){
System.out.println("大家好,我是"+name+" 年龄"+age+" 性别"+sex+" 分数"+score);
}
}

TestThis.java

1
2
3
4
5
6
public class TestThis {
public static void main(String[] args) {
Student stu = new Student("xiaoming",18,"男",98.0);
stu.sayHi();
}
}

运行结果:

image-20220203174917953