面向对象的介绍

写程序的套路

  • 面向:拿、找
  • 对象“能干活的东西
  • 面向对象编程:拿东西过来做对应的事情

当我们想要在代码当中完成一件事情的时候,我们是拿对应的东西来做这件事情

面向对象编程的例子

package Test;import java.util.Random;
import java.util.Scanner;public class Test {public static void main(String[] args) {// 1,得到一个随机数对象,用于得到随机数Random r = new Random();int data = r.nextInt(10) + 1;  // 生成1~10之间的随机数System.out.println(data);// 2,创建一个Scanner对象, 用于接收用户输入的数据Scanner sc = new Scanner(System.in);System.out.println("请输入您的年龄:");int age = sc.nextInt();System.out.println(age);}
}

看这段代码中

Random r = new Random(); :需要随机数的时候,使用Random可以生成随机数

System.out.println(data);:需要往控制台输出的时候,使用System

Scanner sc = new Scanner(System.in); :需要键盘录入,使用Scanner

在现在我写的代码当中用到最多的就是这三个RandomSystemScanner。以后用的多了,就直接去找,然后用。

找工具干活。

我想说这里好像python的库,需要什么,就去下载对用的库,用里面的函数,也可以自己写。

为什么用面向对象编程

我要洗衣服,那我就要用洗衣机

我要扫地,那我就要使用扫地机器人

我要找朋友聊天,那我就要用手机

我要干什么,那我就是找对应的工具。这个思维就像上面的代码,我要生成随机数,那我就要用Random。需要什么,就用什么

符合人类思维习惯,编程更简单,更好理解。

面向对象编程到底学什么

  • 学习获取已有对象并使用

    RandomSystemScanner

  • 学习自己设计对象并使用

    设计对象并使用、封装、this关键字、构造方法、标准JavaBean、对象内存图、补充知识:变量成员变量、局部变量区别

    什么意思呢。

    我要生成随机数,那就用Java已经准备好的Random

    如果我要是用别的,比如我想使用锤子,但是我没有锤子,我就要自己在造,然后使用

    我为啥感觉这里好像是函数、方法,如果没有这个函数或者方法,那我就定义一个。

类和对象

  • 类(设计图):是对象共同特征的描述
  • 对象:是真实存在的具体东西

在Java中,必须先设计类,才能获得对象。

这里怎么说呢,就是说洗衣机,洗衣机也是被设计出来的,想要设计洗衣机,那么就要有洗衣机的图纸

想要盖房子,那就需要房子的图纸,没有图纸,你怎么盖呢

这么一个例子,定义一个手机类,里面空的

public class Phone{}

通过new Phone()就会出现一部手机,再new Phone()还会出现一部手机

    • 类的理解

      • 类是对现实生活中一类具有共同属性和行为的事物的抽象
      • 类是对象的数据类型,类是具有相同属性和行为的一组对象的集合
      • 简单理解:类就是对现实事物的一种描述
    • 类的组成
      • 属性:指事物的特征,例如:手机事物(品牌,价格,尺寸)
      • 行为:指事物能执行的操作,例如:手机事物(打电话,发短信)
  • 类和对象的关系
    • 类:类是对现实生活中一类具有共同属性和行为的事物的抽象
    • 对象:是能够看得到摸的着的真实存在的实体
    • 简单理解:类是对事物的一种描述,对象则为具体存在的事物

【以上来自老师markdown笔记】

如何定义类

public class 类名 {1, 成员变量(代表属性。一般是名词)2, 成员方法(代表行为。一般是动词)3, 构造器4, 代码块5, 内部类}
  • 成员变量:属性,名词;就是说手机的牌子、价格、颜色…都属于属性
  • 成员方法:行为,动词;手机可以打电话、发短信、玩游戏…

定义类

package ClassDemp;public class Phone {// 属性String brand;  // 手机的品牌double price;  // 价格// 方法public void call() {System.out.println("手机在打电话");}public void playGame() {System.out.println("手机在玩游戏");}
}

定义一个手机类,因为类表示的是一类事物,并不是真正的手机,使用属性只定义,不赋值

如何得到类的对象

类名 对象名 = new 类名

Phone p = new Phone;

对上面的手机类进行创建

package ClassDemp;public class PhoneTest {public static void main(String[] args) {// 创建手机对象Phone p = new Phone();}
}

如何访问对象

  • 访问属性:对象名.成员变量
  • 访问方法:对象名.方法名(...)
package ClassDemp;public class PhoneTest {public static void main(String[] args) {// 创建手机对象Phone p = new Phone();// 给手机赋值p.brand = "oppo";  // 品牌p.price = 2599;  // 价格// 获取手机的值System.out.println(p.brand);System.out.println(p.price);// 调用手机的方法p.call();  // 打电话p.playGame();  // 玩游戏}
}

那如果是两部手机呢

package ClassDemp;public class PhoneTest {public static void main(String[] args) {// 第一部手机// 创建手机对象Phone p = new Phone();// 给手机赋值p.brand = "oppo";  // 品牌p.price = 2599;  // 价格// 获取手机的值System.out.println(p.brand);System.out.println(p.price);// 调用手机的方法p.call();  // 打电话p.playGame();  // 玩游戏// 第二部手机Phone p2 = new Phone();p2.brand = "iphone";  // 品牌p2.price = 10000;  // 价格System.out.println(p2.brand);System.out.println(p2.price);p2.call();  // 打电话p2.playGame();  // 玩游戏}
}

new两下。

类的几个补充注意事项

  • 测试类与JavaBean类

    • 用来描述一类事物的类,专业叫做:JavaBean类

      在JavaBean类中,是不写main方法的

    • 在以前,编写main方法的类,叫做测试类

      我们可以在测试类中创建JavaBean类的对象并进行赋值调用。

    我还想怎么在Phone类的时候没有写main方法,这是为什么呢,Phone类属于JavaBean类,不需要main方法

    而写的PhoneTest类中,这个是测试类,需要主方法,同时还在这个类里面创建了Phone对象。

  • 定义JavaBean类的注意事项

    首先回顾一下定义类的格式

    public class 类名 {1, 成员变量(属性)2. 成员方法(行为)
    }
    

    定义一个学生类

    public class Student {// 属性(成员变量)String name;  // 姓名double height;  // 身高// 行为(方法)public void study {// 学习}public void sleep {// 睡觉}
    }
    

    定义一个学生类,

    接下来,就是一些规则

    • 类名首字母建议大写,需要见名知意,驼峰模式、

    • 一个Java我就中可以定义多个class类,且只能一个类是public修饰,而且public修饰的类名必须称为代码文件名

      实际开发中建议还是一个文件定义一个class类,

    • 成员变量的完整定义格式是:

      修饰符 数据类型 变量名称 = 初始化值;

      一把无需指定初始化值,存在默认值

    驼峰式命名,这个熟悉,我在写代码的时候一直符合,就是有时候英语单词忘拼了、

    第二点我没有太明白,可以定义多个类,但是只有第一个类public修饰,我没有尝试过,不知道。

    然后是定义多个类时,文件名必须是public修饰的类的类名,既然有多个类了,名字是类里面的一个,这样看着好难受。我还是老老实实一个文件一个类把。

    成员变量的定义格式,修饰符我不知道,然后有个初始化值,但是我在定义的时候没有初始化值,因为如果我给一个初始化值String name = "zhangsan"。这个学校真奇怪,里面的学生都是一个名字。

    如果不写,会有一个默认值。

    • 对象的成员变量的默认值规则

      数据类型 明细 默认值
      基本类型 byte、short、int、long 0
      float、double 0.0
      Boolean false
      引用类型 类、接口、数组、String null

    学生类中的姓名是String类型,默认值是null。

练习

女朋友类

编写女朋友类,创建女朋友类的对象

给女朋友的属性赋值并调用女朋友类中的方法。

我们先看JavaBean类文件

package ClassDemp;public class GirlFriend {// 成员变量(属性)String name;  // 姓名int age;  // 年龄// 成员方法(行为)public void sleep() {System.out.println("女朋友在睡觉");}public void playGame() {System.out.println("女朋友在玩游戏");}public void cook() {System.out.println("女朋友在做饭");}}

再来看Java测试类文件

package ClassDemp;public class GirlFriendTest {// 主方法;测试类public static void main(String[] args) {// 创建一个女朋友对象GirlFriend girl = new GirlFriend();girl.name = "小红";girl.age = 20;System.out.println(girl.name);System.out.println(girl.age);girl.playGame();girl.sleep();System.out.println("===============");// 创建第二个女朋友GirlFriend girl2 = new GirlFriend();girl2.name = "小明";girl2.age = 22;System.out.println(girl2.name);System.out.println(girl2.age);girl2.cook();girl2.sleep();}
}

两个new,使用创建了两个女朋友。哈哈哈

封装

什么是封装

  • 告诉我们,如何正确设计对象的属性和方法

比如有这么一个需要,

设计一个类描述人,

  • 属性:姓名、年龄
  • 行为:吃饭、睡觉

这个很简单,在上面的代码中就设计过,

public class Person {string name;int age;public void eat(){System.out.println("吃饭");}public void sleep(){System.out.println("睡觉");}}

这个没有什么好说的,就和上面定义手机类是一个道理。

属性是名词、方法是动词

但是编程不可能就是这么简单,需求不可能永远这么单一。一般是多个对象。多个对象就有多个方法。

为了笔记清晰,我先把封装思想的原则写出来

对象代表什么,就得封装对应的数据,并提供数据对应的行为

举例一:人画圆

需求:人画圆,请针对这个需求进行面向对象设计

这么看简单呀,两个对象。那我先设计两个对象,人对象和圆对象

// 人对象
public class Preson {}
// 圆对象
public class Circle {}

一切都是那么简单。

但是问题来了,定义一个画圆的方法,那是人画圆呢,还是圆画圆呢

// 画圆
public viod draw {System.out.println("画圆");
}

这个方法属于谁?人?圆?

我是这么想着,人画圆,我拿着笔画个圆,那应该是人的方法

错了,画圆是圆的方法

// 圆对象
public class Circle {// 画圆public viod draw {System.out.println("画圆");}
}

我把封装思想的原则拉下来。

对象代表什么,就得封装对应的数据,并提供数据对应的行为

定义好了这个圆的类,那就要写圆的属性了。用过圆规都知道,想画圆那就需要半径。

所以圆的第一个属性,

// 圆对象
public class Circle {double radius;  // 半径
}

用上面的思想就是:Circle代表圆,封装了圆的半径,那就要提供画圆的方法

// 圆对象
public class Circle {double radius;  // 半径// 画圆public viod draw {System.out.println("根据半径" + radius + "画一个圆");}
}

人画圆,这个圆到底是人画的,还是圆自己画着呢,那如果说是人画的,画圆这个方法就应该设计到人里面,如果说圆是自己画的,就应该设计到圆这个给对象里面。答案是人画圆,这个圆不是人自己画着,而是圆自己画的,人只是调用了圆的方法去画了一个圆而已。

举例二:人关门

这个好理解,人并没有关门,人只是给门一个作用力,然后们就自己关了,开门也是,人只是负责给一个作用力。、

// 门
public class Door {public void open() {...}public void close() {...}
}

对象是门,那么就应封装门状态的数据,因为封装了门的状态,那么就要提供和这个数据想关的开门关门的方法

// 门
public class Door {boolean flag = true;  // 门的状态数据// 开门public void open() {...}// 关门public void close() {...}
}

举例三:张三砍了李四

张三砍了李四,李四就凉凉了,或者是生命值为0.

那么李四是自己凉的?还是张三砍的。答案是李四是自己凉的

也可以通过这条规矩可以看出来:XX持刀行凶,造成了XX死亡的后果、

就是说XX是自己凉的,只是XX造成的,放到这个里面就是李四是自己凉的,只不过是张三造成的。

因此:凉凉这个行为应该设计到李四那里。

张三砍了李四,用程序的画来说是张三调用了李四凉凉的方法。

总结

封装最基本的思想

对象代表什么,就得封装对应的数据,并提供数据对应的行为

老师举例的三个例子我记录了下来,第一次学习,面向对象的三大特征封装、继承、多态就我而言不是一下子就能学会的,所以这只是第一次学习。

我是这么理解的,半径、门的状态、凉凉这些都可以看成属性。这个属性在这个类里面,那就要把想过方法设计到里面。

  • 人画圆:半径属于圆。要根据半径画圆,所以画圆是半径的方法。人画圆应该是是人用什么工具画圆,铅笔、钢笔、等等
  • 人关门:门的状态就是属于门的,那是就是提供与关门开门想关的方法
  • 张三砍李四,凉凉是李四的状态,那么就要提供方法

数据是谁的,那你就要提供与这个数据相关的方法。

理解封装思想的好处

  • 让编程变得很简单,有什么事,找对象,调用方法就行。
  • 降低我们的学习成本,可以少学,少记,或者说压根不用学,不用记对象有哪些方法、有需要时去找就行。

他是有自己自带的对象的,需要的时候就去用就可以。比如

package ClassDemp;public class Demo {public static void main(String[] args) {String  str = "asdfghjkl";int len = str.length();  // 字符串长度System.out.println(len);  // 9String str2 = str.toUpperCase();  // 字符串大写System.out.println(str2);  // ASDFGHJKL}
}

需要什么就去找,Java提供了一个文档,里面记得很全,就像你不认识这个字,那你就要查字典。

这一点怎么好像python,python为什么火,那就是第三方库,需要什么就去下载第三方库,然后调用。

private关键字

  • 是一个权限修饰符
  • 可以修饰成员(成员变量华人成员方法)
  • 被private修饰的成员只能在本类中才能访问,
  • 针对private修饰的成员变量,如果需要被其他类使用,提供相应的操作。
  • 提供setXxx(参数) 方法,用于给成员变量赋值,方法用public修饰
  • 提供getXxx(参数) 方法,用于获取成员变量的值,方法用public修饰

这个是什么意思呢,private,先介绍一下这个单词是什么意思,私有,我自己的。也就可以看懂了什么是只能在本类中访问。

先说一下我们之前写的女朋友类。代码如下

  • JavaBean类

    package ClassDemp;public class GirlFriend {// 成员变量(属性)String name;  // 姓名int age;  // 年龄// 成员方法(行为)public void sleep() {System.out.println("女朋友在睡觉");}public void playGame() {System.out.println("女朋友在玩游戏");}public void cook() {System.out.println("女朋友在做饭");}}
    
  • Java测试类

    package ClassDemp;public class GirlFriendTest {// 主方法;测试类public static void main(String[] args) {// 创建一个女朋友对象GirlFriend girl = new GirlFriend();girl.name = "小红";girl.age = 20;System.out.println(girl.name);System.out.println(girl.age);girl.playGame();girl.sleep();}
    }
    

首先要说明这个代码是不安全的。咱就是说年龄,年龄是一个正整数,而int的范围包括负数。所以有个赋值成负数,那是不报错的,但是会不合理,我今年-18岁,这是什么意思?没有出生?

那这个问题怎么解决呢,我写if就可以了。如果年龄是个负数或者不合理就抛出一个提示,可以,但是说如果有好几个女朋友呢,这么写,代码阅读性差,低效率。这是其一,

还有其二,封装的思想是封装什么数据,就应该提供相关的方法。那你封装了年龄,那就应该提供一个验证年龄是否符合的方法。

具体怎么解决。让属性都不可见。私有。对外面提供方法,赋值,获取,

我们先看修改后的JavaBean类

package ClassDemp;public class GirlFriend {// 成员变量(属性)private String name;  // 姓名private int age;  // 年龄// 成员方法(行为)// 对姓名就行赋值public void setName(String n) {name = n;}// 获取姓名public String getName() {return name;}// 对年龄进行赋值public void setAge(int a) {// 对数据进行验证if ((a>=0) && (a<=50)) {age = a;}else {System.out.println("数据赋值错误");}}// 获取年龄public int getAge() {return age;}public void sleep() {System.out.println("女朋友在睡觉");}public void playGame() {System.out.println("女朋友在玩游戏");}
}

public:公共;private:私有,一对反义词

在属性前面全部添加了private,表示在Java的测试类中是无法直接通过girl.name 这种方式访问的,这样就保证了数据的合法性。

虽然属性不可见,,但是方法可见,在方法前面设置的是public。

应该怎么访问,这个可能大家已经猜到。就是通过方法的方式进行访问内部属性

Java测试类

package ClassDemp;public class GirlFriendTest {// 主方法;测试类public static void main(String[] args) {// 创建一个女朋友对象GirlFriend gf = new GirlFriend();// 赋值gf.setName("小红");gf.setAge(19);System.out.println(gf.getName());System.out.println(gf.getAge());gf.playGame();gf.sleep();}
}

这就是像之前在测试类中使用方法一样。

用过setName()setAge() 方法对名字和年龄赋值,然同通过getName()getAge()获取年龄、姓名

但是要注意这里

// 对年龄进行赋值
public void setAge(int a) {// 对数据进行验证if ((a>=0) && (a<=50)) {age = a;}else {System.out.println("数据赋值错误");}
}

这里有个逻辑处理,也很简单。

如果是正常赋值gf.setAge(19);

小红
19
女朋友在玩游戏
女朋友在睡觉

错误gf.setAge(-19);

数据赋值错误
小红
0
女朋友在玩游戏
女朋友在睡觉

就近原则和this关键字

成员变量和局部变量

public class GirlFriend {private int age;  // 成员变量  public void method() {int age = 10;  // 局部变量System.out.println(age);  // ???}
}

首先,介绍两个名词,成员变量和局部变量

  • 成员变量:定义在类中,方法外面。
  • 局部变量:定义在方法里面、代码块中,或者方法的声明上。

虽然在这段代码中定义了两个age,但是互不冲突。

就近原则

public class GirlFriend {private int age;  // 0  public void method() {int age = 10; System.out.println(age);  // ???}
}

那我现在输出age,输出的是第一age,还是第二个age

这里就用到了就近原则,什么就就近原则

  • 谁离我近,我就用谁。

所以

System.out.println(age);  // 10

因为离 System.out.println(age); 最近的是int age=10;

如果对代码进行改动,让方法体的内容位置互换

public class GirlFriend {private int age;  // 0  public void method() {System.out.println(age);  // ???int age = 10; }
}

那么

System.out.println(age);  // 0

因为离 System.out.println(age); 最近的是private int age;,此时age没有赋值,默认值为0

this关键字

我们再看女朋友JavaBean类的代码(节选)

// 对姓名就行赋值
public void setName(String n) {name = n;
}// 对年龄进行赋值
public void setAge(int a) {// 对数据进行验证if ((a>=0) && (a<=50)) {age = a;}else {System.out.println("数据赋值错误");}
}

这里有两个地方看着很别扭,na代表什么,因为是我刚刚写的,所以我做的,n是姓名、a年龄。

如果改成这样

private String name;  // 姓名
private int age;  // 年龄// 对姓名就行赋值
public void setName(String name) {name = name;
}// 对年龄进行赋值
public void setAge(int age) {// 对数据进行验证if ((age>=0) && (age<=50)) {age = age;}else {System.out.println("数据赋值错误");}
}

那么就会赋值失败,因为出发了就近原则,

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

赋值就是把右边的数据给左边。这时左边的nameString name最近,够不到private String name;

也就是说左边的name = 右边的name = String name

private String name;为空

但是写个n很容易引起误会。这时怎么解决

this.成员变量 通过this关键字指定,直接使用成员变量,而不是局部变量。

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

我重新修改一下完整代码

  • JavaBean类

    package ClassDemp;public class GirlFriend {// 成员变量(属性)private String name;  // 姓名private int age;  // 年龄// 成员方法(行为)// 对姓名就行赋值public void setName(String name) {this.name = name;System.out.println(name);}// 获取姓名public String getName() {return name;}// 对年龄进行赋值public void setAge(int age) {if ((age>=0) && (age<=50)) {this.age =age;}else {System.out.println("数据赋值错误");}}// 获取年龄public int getAge() {return age;}public void sleep() {System.out.println("女朋友在睡觉");}public void playGame() {System.out.println("女朋友在玩游戏");}
    }
  • Java测试类

    package ClassDemp;public class GirlFriendTest {// 主方法;测试类public static void main(String[] args) {// 创建一个女朋友对象GirlFriend gf = new GirlFriend();// 赋值gf.setName("小红");gf.setAge(19);System.out.println(gf.getName());System.out.println(gf.getAge());gf.playGame();gf.sleep();}
    }

构造方法

构造方法概述

  • 构造方法也叫作构造器、构造函数
  • 作用在创建对象的时候给成员变量进行初始化(赋值)的

在以前我们是这么写代码的

public class StudentDemo {// 主方法;测试类public static void main(String[] args) {Student s1 = new Student();}
}

注意这里Student s1 = new Student();中的Student();()什么都不写,表示调用的是一个空参的构造方法

构造方法的格式

public class Student {修饰器 类名(参数) {方法体;}
}
  • 特点

    1. 方法名和类名相同,大小写也要一致
    2. 没有返回类型,连void都没有
    3. 没有具体的返回值(不能由return带回结果)
  • 举例

    public class Student {private String name;private int age;// 空参构造方法    public Student() {...}// 带全部参数的构造方法    public Student(String name, int age) {...}
    }
    

    空参构造,成员变量默认初始化值,

    带参构造,在方法里面,直接给成员变量赋值,就不需要用set()赋值

  • 执行时机

    1. 创建对象的时候由虚拟机调用,不能手动调用构造方法
    2. 每创建一次对象,就会调用一次构造方法

    也就是说,如果我创建10次,那么虚拟机也会调用10次构造方法

无参构造方法示例

首先,展示JavaBean类代码

package ClassDemp;public class Student {private String name;  // 姓名private int age;  // 年龄// 对姓名赋值public void setName (String name) {this.name = name;}// 获取姓名public String getName() {return name;}public void setAge(int age) {if (age>=0) {this.age = age;}else {System.out.println("数据错误");}}public int getAge() {return age;}
}

首先,我在测试类中调用一个空参构造

package ClassDemp;public class StudentTest {public static void main(String[] args) {// 空参构造Student stu1 = new Student();}
}

此时,JavaBean类中并没有创建这个空参构造,

如果我们自己没有写任何构造方法,那么虚拟机给我们加一个空参构造方法

什么意思呢,此时,我们没有在JavaBean类中写任何构造方法,那么虚拟机就会给我构造一个默认的无参构造方法,怎么样呢,我们其实也可以把这个无参的构造方法给写出来。

// 无参构造(默认)
public Student() {}

注意,这种无参构造,默认的只能适用于

Student stu1 = new Student();

可以验证一下,

在JavaBean类中添加如下代码

// 无参构造(默认)
public Student() {System.out.println("我被执行了");
}

然后在测试类中代码不变。

package ClassDemp;public class StudentTest {public static void main(String[] args) {// 空参构造Student stu1 = new Student();}
}

运行代码就会发现输出

我被执行力

那么什么意思呢,就是说 Student stu1 = new Student(); 就会直接调用那个无参构造方法。

没有给name、age成员变量进行赋值,如果我们执行

System.out.println(stu1.name);  // null
System.out.println(stu1.age);  // 0

返回默认值

含参构造方法示例

还是上面的那段代码,

  • JavaBean

    package ClassDemp;public class Student {private String name;  // 姓名private int age;  // 年龄// 无参构造public Student() {System.out.println("我被执行了");}// 含参构造public Student(String name, int age) {this.name = name;  // 对成员变量name赋值this.age = age;  // 对成员变量age赋值}// 对姓名赋值public void setName (String name) {this.name = name;}// 获取姓名public String getName() {return name;}public void setAge(int age) {if (age>=0) {this.age = age;}else {System.out.println("数据错误");}}public int getAge() {return age;}
    }
    
  • Java测试类

    package ClassDemp;public class StudentTest {public static void main(String[] args) {// 含参构造Student stu2 = new Student("张三", 20);System.out.println(stu2.getName());  // 张三System.out.println(stu2.getAge());  // 20}
    }

这段代码怎么看呢,首先我在JavaBean类创建了一个含参构造方法,

// 含参构造
public Student(String name, int age) {this.name = name;  // 对成员变量name赋值this.age = age;  // 对成员变量age赋值
}

那这个方法我应该怎么使用呢,在测试类中

Student stu2 = new Student("张三", 20);

就这样直接给成员变量赋值了。之前我们在通过对象名.成员变量stu1.name)或者是用set("张三") 方法进行赋值的,

而现在直接一句话Student stu2 = new Student("张三", 20);进行赋值。

他是怎么赋值呢?

当我们在创建对象(stu2)的时候,系统会在自动的根据后面的参数("张三", 20)去调用自己的构造。而现在这里有两个参数,所以他就会调用对应的有参构造。他会把张三 20传递过去,nameage进行接收,然后赋值给成员变量的name``age

这里很类似方法,方法就是这样,传一个值,然后再方法体里面进行操作,之后返回,对,构造方法没有返回值。

System.out.println(stu2.getName());  // 张三
System.out.println(stu2.getAge());  // 20

可以输出成员变量的值。

构造方法注意事项

  1. 构造方法的定义

    • 如果没有定义构造方法,系统将给出一个默认的无参构造方法
    • 如果定义了构造方法,系统不再提供默认的构造方法

    第一条我们已经不陌生了,在没有任何构造方法的时候,使用Student stu2 = new Student();虚拟机会默认生成一个无参构造方法

    那第二条呢,首先JavaBean文件

    package ClassDemp;public class Student {private String name;  // 姓名private int age;  // 年龄// 含参构造public Student(String name, int age) {this.name = name;  // 对成员变量name赋值this.age = age;  // 对成员变量age赋值}
    }

    测试类

    public class StudentTest {public static void main(String[] args) {// 含参构造Student stu2 = new Student();}
    }
    

    此时会报错

    java: 无法将类 ClassDemp.Student中的构造器 Student应用到给定类型;
    

    因为没有默认的构造方法,为什么?因为定义了一个含参的构造方法,那么系统不提供默认的构造方法。此时Student会调用无参构造,但是找不到。

  2. 构造方法的重载

    带参构造方法,和无参数构造方法,两者方法名相同,但是参数不同,这叫做构造方法的重载

    这里很像方法的重载

  3. 推荐的使用方式

    无论是否使用,都需要手动书写无参构造方法和带全部参数的构造方法。

    不仅只是为了自己,还要考虑别人。

构造方法扫盲

构造方法就是来创建对象的

结论:错

  • 构造方法概述

    创建对象的时候,询价会自动调用构造方法,作用是给成员变量进行初始化的

public class StudentTest {public static void main(String[] args) {Student s1 = new Student();}
}

真正创建对象是new关键字做的,虚拟机在创建对象的时候其实是由很多很多步骤的,而其中调用构造方法只是创建对象的一部分,而这一步就是给成员变量赋初值

就是说构造方法只是一个对成员变量赋初值,并不能创建对象,而创建对象的工作是nwe的,分工明确。

标准的JavaBean类

JavaBean类规则

  1. 类名需要见名知意,大驼峰式命名
  2. 成员变量使用private修饰
  3. 提供至少两个构造方法
    • 无参构造方法
    • 带全部参数的构造方法
  4. 成员方法
    • 提供每一个成员变量对应的setXxx()/getXxx() 方法
    • 如果还有其他行为,也需要写上

这就是一种规则,规范,因为如果是你有一种习惯,我有一种习惯,开发不是一个人的事情,那这样代码就不互通了,也不便于阅读、维护。

【练习】用户登录界面模拟

  • JavaBean类

    package ClassDemp;public class Uesr {private String username;  // 用户名private String password;  // 密码private String email;  // 邮箱private String gender;  // 性别private int age;  // 年龄// 无参构造方法public Uesr() {}// 带全参构造方法public Uesr(String username, String password, String email, String gender, int age) {this.username = username;this.password = password;this.email = email;this.gender = gender;this.age = age;}// get、set方法public void setUsername(String username) {this.username = username;}public String getUsername() {return username;}public void setPassword(String password) {this.password = password;}public String getPassword() {return password;}public void setEmail(String email) {this.email = email;}public String getEmail() {return email;}public void setGender(String gender) {this.gender = gender;}public String getGender() {return gender;}public void setAge(int age) {this.age = age;}public int getAge() {return age;}
    }
  • 测试类

    package ClassDemp;public class UesrTest {public static void main(String[] args) {// 创建第一个用户对象,Uesr uesr1 = new Uesr();  // 无参构造uesr1.setUsername("小明");uesr1.setPassword("xiaoming");uesr1.setEmail("xiaominh@user.com");uesr1.setGender("男");uesr1.setAge(20);System.out.println("=====第一个用户=====");System.out.println(uesr1.getUsername());  // 小明System.out.println(uesr1.getPassword());  // xiaomingSystem.out.println(uesr1.getEmail());  // xiaominh@user.comSystem.out.println(uesr1.getGender());  // 男System.out.println(uesr1.getAge());  // 20// 创建第二个对象Uesr uesr2 = new Uesr("小红", "xiaohong", "xiaohong@uaer.com", "女", 18);  // 带全参构造System.out.println("=====第二个用户=====");System.out.println(uesr2.getUsername());  // 小红System.out.println(uesr2.getPassword());  // xiaohongSystem.out.println(uesr2.getEmail());  // xiaohong@uaer.comSystem.out.println(uesr2.getGender());  // 女System.out.println(uesr2.getAge());  // 18}
    }

这可以看作是一个完整的JavaBean文件和测试类文件,在日常写代码的时候也应该遵循这个规则

分享一个快捷键和一个插件:

  • Alt + Insert(Alt + Fn + Insert):快速生成构造函数
  • PTG插件:鼠标右击,pig to JavaBean(Ctrl + Shift + 逗号)

三种情况的对象内存图

首先,回顾两张图片

这两张图什么意思,已经并不陌生,不管打开什么软件,都会在内存中占用一块内存,Java也是。

Java为了合理利用这块内存,所以又把这块内存分成了五个区域,本地方法栈、寄存器、栈、方法区、堆。(如图所示)

每一块都有自己的作用,

JDK7以前,方法区和堆空间是一块连续的空间,而再8以后,堆和方法区分开了,这个我也在前面写个。

到现在我们已经了解了栈、堆,

  • 栈:方法在里面执行
  • 堆:new出来的东西存储在里面,会有一个地址值。

现在,对方法区做个笔记。

方法区本身有很多的作用,有很多的功能。其中当我们运行一个类的时候,这个类的字节码文件就会被加载到方法区当中临时存储。

首先有两个文件HelloWorld.classTest.class, 当被执行时,都会加载到方法区进行临时存储,然后如果调用了某个方法,那么方法就会进栈,方法执行完毕,就会出栈。然后对空间就是用来存放new出来的东西,然后会有一个地址值。

一个对象的内存图

文字简述

Student s = new Student();

如果这段代码被执行。那么内存会做至少七件事

  1. 加载class文件:Student类的字节码Student.class 文件会加载到内存。
  2. 声明局部变量:s
  3. 在堆内存中开辟一个空间:因为有new,这个空间也就是对象
  4. 默认初始化
  5. 显示初始化
  6. 构造方法初始化
  7. 将堆内存中的地址值赋值给左边的局部变量

什么意思呢?我们在创建这个Student对象的时候,肯定已经写好了Student类,因为如果你没写好,肯定会报错。

执行这句话,首先这个类会被加载到内存,先不管是哪块内存。内存加载完后,会声明一个局部变量s。因为看到了new,所以肯定会用到堆内存。没有给成员变量赋初值,所以,默认初始化就0null…,其实3、4、5步的初始化就是对这个开辟的空间赋值的。然后再把地址值给变量s

怎么理解int[] arr = new int[5]这个不陌生,arr存储的就是地址值,

那么Student s = new Student();s 存储的也是地址值

图文叙述

首先,准备两段代码

  • JavaBean

    package ClassDemp;public class Student {private String name;  // 姓名private int age;  // 年龄public void study() {System.out.println("好好学习");}
    }
    
  • Java测试类

    package ClassDemp;public class StudentTest {public static void main(String[] args) {Student s = new Student();System.out.println(s);  // ClassDemp.Student@4eec7777System.out.println(s.name + "..." + s.age);  // null...0s.name = "小明";s.age = 18;System.out.println(s.name + "..." + s.age);  // 小明...18s.study();  // 好好学习}
    }
    

准备好两个文件,开始执行代码

首先要明白我们写的JavaBean类Student.java文件是被调用的,而StudentTest.java 测试类是我们执行的。他们的字节码文件分别是:Student.classStudentTest.class

我们执行StudentTest.java文件后,产生的字节码文件StudentTest.class 会被加载到方法区。然后main()就会被临时存储。这个时候虚拟机会自动调用程序的主方法,main() 方法被加载到栈,开始执行。

执行Student s = new Student(); ,这一步里面就有七小步

  1. Student.class被加载到方法区。

  2. 在栈内存里面开辟一片空间,空间名是s,这块空间只能存储Student类的地址值。

  3. 因为new,所以在堆里面也需要开辟空间,同时这个里面是需要存放东西的,先会把Student类里面的成员变量拷贝一份,再会把Student类的成员方法的地址进行存储,存储成员方法的地址是方便以后用对象调用方法的时候可以找到方法。

    因为是在堆里面开辟的,所以,会返回一个地址值4eec7777

  4. 默认初始化,那么,name就是nullage 就是0

  5. 显示初始化,如果一开始,给成员变量进行赋初值,name = 小红age=10那么null和0就会被小红和10替代,因为没有赋初值,所以这里不用管

  6. 现在是空参构造,而且在空参构造里面并没有写任何代码,所以还是可以被忽略。

    如果用的是有参构造,那么此时的name和age也会有值

  7. 再把地址值4eec7777返回给s

这时,一个对象创建完毕。

sout(s) 那么只是输出对象的地址值。因为s存储的是地址值

通过s.name或者s.age 进行访问对象的内部成员。

第一次输出因为没有赋值,所以输出堆对对象的默认初始化值。

然后赋值,输出。

这时调用成员方法s.study() 那么还是和上面一样,通过s去在堆里找成员方法的地址,因为堆里面存储的是成员方法的地址,那还要通过这个地址取方法区找到这个方法,然后stUdy()方法入栈,执行完,出栈。

main()方法也执行完了,然后出栈,那么main方法里的变量也就消失,现在堆里面的对象没有人用了,

没有变量指向这个空间了。

那么这个空间也就消失了。

两个对象的内存图

  • JavaBean文件

    package ClassDemp;public class Student {String name;  // 姓名int age;  // 年龄public void study() {System.out.println("好好学习");}
    }
    
  • Java测试类

    package ClassDemp;public class StudentTest {public static void main(String[] args) {// 第一个对象Student s1 = new Student();System.out.println(s1);s1.name = "小明";s1.age = 18;System.out.println(s1.name + "..." + s1.age);s1.study();// 第二个对象Student s2 = new Student();System.out.println(s2);s2.name = "小红";s2.age = 20;System.out.println(s2.name + "..." + s2.age);s2.study();}
    }

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-oY1NW7z8-1661157751123)(C:\Users\admin\AppData\Roaming\Typora\typora-user-images\image-20220821212540133.png)]

这样画的图会不会比较清晰

这就是两个对象的内存图,从图中可以看出,两个对象互不干扰。我们一一分析。

首先还是StudentTest.class文件加载到方法区,里面的主方法被临时存储,主方法入栈

执行到Student s1 = new Student();还是做那七件事;声明变量、开辟空间,默认初始化等等,然后返回对象在堆里面的内存地址值。

然后执行下面的输出语句。

执行到s1.study()时,还是去找这个方法,从找内存找到堆内存再找到方法区,入栈,方法执行完出栈。

到这里,和一个对象的执行都是一样的,

现在执行到Student s2 = new Student();

此时有一个**重点!!!**那就是class字节码文件不需要在加载一遍,已经在创建第一个对象的时候加载过了。直接用就可以了。

然后还是和之前一样,做那七件事。

然后执行下面语句

直到study()方法执行完出栈,

主方法也被执行完,出栈,两个空间变量消失,因为对象没有被使用,也就成为垃圾。

可以说是两个对象,两块空间,互不干扰。

两个引用指向同一个对象

  • JavaBean

    package ClassDemp;public class Student {String name;  // 姓名int age;  // 年龄public void study() {System.out.println("好好学习");}
    }
  • Java测试类

    package ClassDemp;public class StudentTest {public static void main(String[] args) {Student s1 = new Student();s1.name = "小明";Student s2 = s1;s2.name = "小红";System.out.println(s1.name + "..." + s2.name);}
    }
    

代码又变了,注意这里Student s2 = s1;那么他们的内存图会怎么样

先说结论,因为s2 = s1,而s1存储的是一个地址值,所以s2存储的就是s1的地址值。s2 = s1 = 001,共有一块空间,那么不管谁改变,那么另一个都会改变,这段代码也在之前见过

int[] arr1 = {1, 2, 3, 4, 5};
int[] arr2 = arr1;

指向同一个地址。

他的内存图,可以看出,无论是s1还是s2都指向的是同一块内存,所以共用。要变一起变,不变就都不变

然后接着执行这段代码

s1 = null;
System.out.println(s1.name);  // NullPointerException

会报错,

先说这段代码,什么是null,空。不存在的空间,就是说,之前s1 = 001,现在我把001覆盖了,改成了null。这么一来,s1指向的是一个不存在的空间,那你觉得s1.name还会有值吗?

NullPointerException:空指针异常。

// System.out.println(s1.name);  // NullPointerException
System.out.println(s2.name);  // 小红
s2 = null;

把上面那句话注释掉,然后输出s2.name此时,正常输出,

s2 = null同理,一个空指针。

虽然指向的是同一个空间,但是还是两个不同的变量。s1被覆盖了,但是s2正常。

this的内存原理

  • this的作用:区分局部变量和成员变量
  • this的本质:代表方法调用者的地址值

this的作用,上面已经提到过,就是如何解决就近原则的问题,那他的原理又是怎么样的,我们需要深究一下。

一个对象的this内存原理

还是两个文件

  • JavaBean

    package ClassDemp;public class Student {private int age;  // 年龄public void method() {int age = 10;System.out.println(age);  // 10System.out.println(this.age);  // 0}
    }
    
  • 测试类

    package ClassDemp;public class StudentTest {public static void main(String[] args) {Student s = new Student();s.method();}
    }
    

Student s = new Student(); 这里,还是那七件事,不变,然后调用s.method()方法。

在这里要清楚一个事,谁调用的method() 方法,答案是s,它的地址是001, 所以调用者是s(001)

因为调用了method()方法,所以方法入栈,

int age = 10,就是在栈里面开辟内存,等等乱七八糟的事情。不说了。

执行到sout(age),因为触发了就近原则,所以输出的是10

执行sout(this.age)

this的作用是用来区分变量,而他的本质是代表方法调用者的地址值。方法调用者,那么是谁调用的method(), 是s调用的,那么s的地址值是001,此时this等价于001.

this.age的意思就是我要获取001里的age。

多个对象的this内存原理

  • JavaBean

    package ClassDemp;public class Student {private String name;public void setName(String name) {this.name = name;}
    }
  • 测试类

    package ClassDemp;public class StudentTest {public static void main(String[] args) {Student s1 = new Student();s1.setName("张三");Student s2 = new Student();s2.setName("李四");}
    }
    

这次创建两个对象,其实和上面的一样,知道了方法的调用者是谁,那么this就是指向谁,就可以修改这个里面的值了。

直接看重点

其实只要明白在主方法里面调用的两次setName()方法分别是谁调用的,就可以这个赋值是什么情况了。

s1.setName(),是s1调用的,s1的地址是001,那么this就是指的是001,001.name就修改了name

s2.setName(),s2地址是002,那么002.name

还有一点,在内存当中方法的形参也是一个变量,变量值是由实参传过来的。

要说this 的原理,那就是地址值,是谁调用的,就是谁的地址值。

this的本质:代表方法调用者的地址值

成员变量和局部变量的区别

package ClassDemp;public class Student {String name;  // 成员变量public void study() {int i = 0;  // 局部变量System.out.println("好好学习");}public void DoHomework() {System.out.println("多做练习");int j =0;  // 局部变量}int age;  // 成员变量
}

什么是成员变量,什么是局部变量,

那我们张口而出,成员变量定义在类中,方法外,如代码中的nameage。成员变量没有先后顺序,只是为了方便阅读,我们习惯吧成员变量写在上面。

那剩下的ij就是局部变量了,定义在方法中。

当然不止这一种区别

区别 成员变量 局部变量
类中位置 类中、方法外 方法内、方法声明之上(形参)
初始化值 有默认初始化值 没有,使用之前需要完成赋值
内存位置 堆内存 栈内存
生命周期 随着对象的创建而存在,随着对象的消失而消失 随着方法的调用而存在,随着方法的运行结束而消失
作用域 整个类中有效 当前方法中有效

也是可以画图表示

  • JavaBean

    package ClassDemp;public class Student {private String name;private int age;
    }
    
  • 测试类

    package ClassDemp;public class StudentTest {public static void main(String[] args) {int a = 10;Student s1 = new Student();}
    }
    

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wAJw0wMQ-1661157751124)(https://cdn.staticaly.com/gh/yanghuanh1314/MyPicture@master/20220822/image.1i0386yjg7ls.webp)]

main()方法执行完,出栈,变量a也就消失了。又因为没有变量指向对象,变量也会消失。

总结

前面的学习,都是每个语言都会有的,数据类型,判断,循环等等

这里,面向对象,也可以说是面向对象编程语言都会有的,但是我没有系统的接触过,即使是python,我也不知道这些事情。

就是只知道面向对象很难。不算难,我感觉我行了

什么是面向对象,一句话,万物皆对象。人、猫、狗;男朋友、女朋友;手机、电脑……

但这样完了吗,没有,设计房子、飞机无论设计什么,你都要有个草图,图纸。在生活中。有时候我们就习惯把需要做的事情记下来,一步一步实施。那么类和对象,类就是图纸,对象就是你根据图纸设计出来的东西。

成员变量就是名词,特征,高的,矮的、品牌、价格……

成员方法就是动词,行为,吃饭、睡觉、打电话、玩游戏……

封装,就是把一些与这个数据相关的操作封装进这个类中,人会吃饭,睡觉、手机可以打电话、发短信。把这些行为都封装到各自的类里面,但要注意,这里有点绕口,却是最最最重要的部分,人用手机玩游戏,那么玩游戏是谁的行为。手机?人?手机。我用手机玩游戏,我调用了手机类的玩游戏这个方法,玩游戏的行为归属于手机。这里有点绕口,看呢一开始还是似懂非懂。我也是,先这样吧,没准一辈子都学不会,没准多多练习,就会了。

在这里有个公开和私有,这是为了代码的安全,同时要提供seXxxt()getXxx()方法

用什么区分成员变量和局部变量,this关键字。然后怎么样就会触发就近原则,怎么解决。

构造方法,要我说,这就是一个特殊的方法,如果没有构造方法,那就要通过setXxx()这种方式进行赋值,不是很方便,因此有了构造方法,Java真是优雅、代码中应该提供两个构造方法,一个无参的,一个带全参的。

JavaBean类就是我们写的没有主方法的类,一开始我还想呢。怎么会没有主方法,然后还有一些规范。

然后就是内存图,画画图,也就会明白

  • 栈:是方法运行的地方
  • 堆:是new出来的东西存放的地方
  • 方法区:类文件会加载到里面

这就是画图的好处,就是费时间。

到这里也就结束了。

好了,我的学习笔记到此结束。

里面肯定有许许多多的bug,欢迎大家指出!毕竟这样成长更快。

也感谢大家可以看到这样,如果帮到了你,是我的荣幸。

谢谢大家!

JavaSE面向对象学习笔记相关推荐

  1. JavaSE面向对象学习笔记总结

    1,构造函数: 用于给对象进行初始化,是给与之对应的对象进行初始化,它具有正对性,函数中的一种. 特点: 1. 该函数的名称和所在的类的名称相同 2. 不需要定义返回类型 3. 该函数没有具体的返回值 ...

  2. JAVASE的学习笔记(四)(抽象类,代码块,接口)

    JAVASE的学习笔记(四) 抽象类与接口 JAVASE的学习笔记(四) 代码块 例题: 静态代码块 加载类的方法 手动加载类 抽象类(**只能被继承使用,自己应该无法创建对象**) 重要: 抽象类由 ...

  3. JavaSE进阶学习笔记-目录汇总(待完成)

    声明:此博客来自于黑马程序员学习笔记,并非商用,仅仅是为了博主个人日后学习复习用,如有冒犯,请联系qq208820388立即删除博文,最后,来跟我一起喊黑马牛逼黑马牛逼黑马牛逼 JavaSE进阶学习笔 ...

  4. JAVASE的学习笔记(九)(Properties类和面向接口编程)

    JAVASE的学习笔记(九) Properties类 简单操作(添加,取出) 属性文件读取配置信息(重要) 属性写入信息(追加) 面向接口编程(小程序) Properties类 简单操作(添加,取出) ...

  5. 【JavaSE阶段学习笔记一】数组以及数组之前的知识点

    文章目录 JavaEE知识总结 前言 一.计算机的基础知识(了解即可) 二.常见的DOS指令 1.进入DOS命令 2.常见的DOS命令(熟练使用) 三.Java语言的历史 四.Java语言的平台版本 ...

  6. C# 面向对象学习笔记

    目录 大纲笔记 代码笔记 最近暂且不忙(Working fish),突然想学习下C#,在慕课找到了kong66老师的C#面向对象编程课程,花了3个晚上看完后受益匪浅.整理了一下笔记和代码,以供日后查询 ...

  7. javascript面向对象学习笔记(一)——继承

    最近在学习html5,玩了下canvas,发现js中很多的东西都不太记得了.翻了下笔记后发现还是去图书馆逛逛把,到借阅区找了我一直想看的<javascript design patterns&g ...

  8. javaSE进阶学习笔记

    1.关于java的集成开发环境: eclipse.IntelliJ IDEA等. 其中目前主流的集成开发环境是:IntelliJ IDEA 这只是一个工具,不要让一个工具把你难住了. 开发工具不要使用 ...

  9. .net面向对象学习笔记

    以下只记录学习当中的各知识点的要点: 1.类是抽象的,对象是具体的,对象可以叫做类的实例.类不占内存,对象才占内存. 2.属性与字段.属性是不保存数据的,字段才保存数据,属性可以控制赋值.取值的过程, ...

最新文章

  1. 11、计算机图形学——几何(贝塞尔曲线与曲面)
  2. html 字号 宽度 像素,JS根据设备宽度设置根节点(html)font-size字体大小
  3. 个人总结:性能测试常见问题案例与原因
  4. Blogger建立Blog部落格​​ - Blog透视镜
  5. Mybatis 高级结果映射 ResultMap Association Collection
  6. php 64位编码解码,php base64 编码和解码
  7. 信息学奥赛C++语言: 蛇形方阵1
  8. 理解JS中的声明式与命令式
  9. 马斯克自曝曾寻求苹果600亿美元收购特斯拉 但库克拒绝会面
  10. HDU-1671 Phone List 暴力版 + 字典树
  11. 线程中消费者生产者的实例代码(synchronized关键字)
  12. Delphi语言基础
  13. 概率学A和C公式,Java计算阶乘,不重复三位数
  14. 网课公众号题库接入使用教程
  15. PS2018 cc的下载和安装
  16. 如何做到服务的高并发、高可用?
  17. mina自定义编解码
  18. Artoolkit初级研究手札(2008.12.9)
  19. Java美元符号取值_java:我如何使用printf打印美元符号和带2位小数的双精度值?...
  20. 整理准备使用wireshark、拉米在线解密PDM5生成密钥网址

热门文章

  1. 牙科手柄市场前景分析及研究报告
  2. nltk 自己训练模型例子
  3. mysql报1862_MySQL错误代码1862 your password has expired的解决方法
  4. 飞行堡垒7(FX505GT),触摸板一直显示已禁用无法正常使用
  5. pycharm 激活码
  6. 怎么样获取桌面图标句柄,并用VC完成随意移动桌面图标地位置?
  7. 当输入一个URL的时候用到什么协议?
  8. Figma有中文版啦!!
  9. http url转义字符和特殊字符处理
  10. Tensorflow深度学习实战之(五)--保存与恢复模型