java面向对象 (1)
cpp面向对象:cpp类与对象
面向对象的三大特征:封装、继承、多态。
设计对象并使用
类和对象
- 类:是对象共同特征的描述;
- 对象:真实存在的具体东西;
在java中,必须先设计类,才可以获取对象。
类的举例
我们可以举几个例子:
- 人可以作为一个对象,其属性有:姓名、年龄、身高、体重,行为有走路、跑步、干饭等等。
-
车可以作为一个对象,其属性有方向盘、轮胎、车骨架等等,行为有装货、装人、开车、停车等等。
具有相同性质的对象,我们可以称其为类。如上面的例子,人,属于人类,车,属于车类。
类的创建
成员变量和成员方法
类的创建和方法类似,但是这是一个类,而不是一个方法。
public class <类名> {
<1. 成员变量(代表属性)>;
<2. 成员方法(代表行为)>;
<3. 构造器>;
<4. 代码块>;
<5. 内部类>;
}
我们在创建类的时候,只定义不赋值。(虽然直接赋值也不是语法错误,但是这样不能表示对象,如果赋值相当于赋初始值)
各种数据类型如果不手动设定初始值,则初始值如下:
| 数据类型 | 数据类型关键字 | 默认值 |
|---|---|---|
| 整数类型 | byte、short、int、long |
0 |
| 小数类型 | float、double |
0.0 |
| 字符类型 | char |
'\u0000'(空字符) |
| 布尔类型 | boolean |
false |
| 引用数据类型 | null |
获得类的对象
- 创建类的对象:
<类名> <对象名> = new <类名>(); - 使用对象:
- 访问属性:
<对象名>.<成员变量> - 访问行为:
<对象名>.<方法名>(<形式参数>);
- 访问属性:
举例
假设我们创建一个手机类:
public class Phone {
//<1. 成员变量(代表属性)>;
String brand;
String model;
double price;
String number;
//<2. 成员方法(代表行为)>;
public void call(String phoneNumber) {
System.out.println("正在给 " + phoneNumber + " 打电话...");
}
public void sendMessage(String phoneNumber, String message) {
System.out.println("给 " + phoneNumber + " 发送消息: " + message);
}
}
在这里面,存在三个成员变量:brand、model、price;和两个成员方法:call、sendMessage。
我们可以创建两个手机的对象:
public static void main(String[] args) {
Phone phone_1 = new Phone();
phone_1.brand = "菠萝";
phone_1.model = "17 Pro Max Plus Ultra";
phone_1.price = 19999.99;
phone_1.number = "13988880000";
Phone phone_2 = new Phone();
phone_2.brand = "大米";
phone_2.model = "17 Ultra Pro Max Plus";
phone_2.price = 18888.88;
phone_2.number = "18700008888";
phone_2.call(phone_1.number);
phone_1.sendMessage(phone_2.number, "我是lin,你好鸭~");
}
在这里,我们创建了两个Phone的对象,分别为phone_1和phone_2,并使用这个对象调用成员方法。

说明:对象的创建可以使用构造方法(Java 1.0(JDK 1.0)就存在),在给对象赋值可以简化为如下:
Phone phone_1 = new Phone("菠萝", "17 Pro Max Plus Ultra", 19999.99, "13988880000");Phone phone_2 = new Phone("大米", "17 Ultra Pro Max Plus", 18888.88, "18700008888");
说明
- 定义类有什么需要注意的:
- 类名首字母建议大写、英文、有意义,满足驼峰模式,不能用关键字,满足标志符规定;
- 一个代码文件中可以定义多个类,但是只能一个类是
public修饰的 public修饰的类名必须是Java代码的文件名称。
-
成员变量的格式是什么样的:
-
成员变量的完整定义格式是:
<修饰符> <数据类型> <变量名称> = <初始化值>;修饰符用来控制这个成员变量的访问权限,常见的有:
public:公开的,任何类都可以访问private:私有的,只有本类可以访问protected:受保护的,同包或子类可以访问- 不写(空):默认的(不写修饰符),同包可以访问
- 一般情况下,无需设定初始值。
-
封装
- 封装的作用:正确设计对象的属性和方法。
- 封装的原则:对象代表什么,就封装对应的数据,并提供对应数据的行为。
- 封装的好处:简化编程的过程
- 封装的重要性:
- 使用
private保护数据 - 通过Getter/Setter方法控制访问
- 提高代码安全性和可维护性
- 使用
private 关键字
被private关键字修饰的成员只可以在本类中访问。
为了保证数据安全与面向对象,在创建类的同时,应该将类设置为私有访问,并提供Get、Set等方法来提供数据的访问。
public class myBankCard {
private int cardMoney = 0;
public int setMyMoney(int money) {
if (money > 0) {
cardMoney = cardMoney + money;
return 200;
} else {
return 500;
}
}
public int getMyMoney() {
return cardMoney;
}
}
this关键字
局部变量和成员变量
- 局部变量:将变量定义在方法中,则这个变量称作“局部变量”;
- 成员变量:将变量定义在方法外面、类里面,则这个变量称作“成员变量”。
我们可以举例如下
public class MyBankCard {
private int money;
public void method() {
String cardNumber;
}
}
在如上代码中,money是成员变量,而cardNunber是局部变量。
当成员变量和局部变量重名
就近原则
不添加this关键字时,遵循就近原则:优先使用局部变量(谁离调用变量的逻辑近,则使用哪个变量)。
public class MyCatFriend {
public static void main(String[] args) {
myCatFriend catFriend = new myCatFriend();
catFriend.method();
}
static class myCatFriend {
private String catName = "linCat";
public void method() {
String catName = "bingCat";
System.out.println(catName);
}
}
}

this 关键字
使用this关键字时,指向当前对象的成员变量。
public class MyCatFriend {
public static void main(String[] args) {
myCatFriend catFriend = new myCatFriend();
catFriend.method();
}
static class myCatFriend {
private String catName = "linCat";
public void method() {
String catName = "bingCat";
System.out.println(this.catName);
}
}
}

this**关键字的三种用法**
- 区分成员变量和局部变量:
this.<变量名> - 调用本类的其他构造方法:
this(<参数>) - 返回当前对象:
return this;
构造方法
构造方法也称作构造器、构造函数。用途是在创建对象的时候给成员变量赋值。
成员变量:定义在方法外面、类里面。
构造方法的特点
- 方法名和类型相同,且大小写敏感。
- 没有返回值类类型(包括
void) - 没有具体返回值:构造方法内可以写
return;语句(不带返回值),用于提前结束构造过程。但不能返回一个值(如return 0;) - 如果不存在独立的构造方法,则JVM虚拟机默认空参构造方法。
构造方法的执行逻辑
- 不可手动调用执行逻辑,在创建对象的时候由虚拟机调用。
- 没创建一次对象,就会自动调用一次构造方法。
说明
- 构造方法也可以重载
- 构造函数需要如果存在参数,则需要同时传入参数。
- 如果存在任何一个构造函数,则JVM虚拟机不再提供任何构造函数(包括默认的无参数构造)
示例
public class MyCatFriend {
public static void main(String[] args) {
myCatFriend catFriend1 = new myCatFriend();
myCatFriend catFriend2 = new myCatFriend("feiCat", 18);
System.out.println(catFriend1.getCatName());
System.out.println(catFriend1.getCatAge());
System.out.println(catFriend2.getCatName());
System.out.println(catFriend2.getCatAge());
}
static class myCatFriend {
private String catName;
private int catAge;
public int setCatName(String catName) {
this.catName = catName;
return 200;
}
public String getCatName() {
return catName;
}
public int setCatAge(int catAge) {
if (catAge < 0 || catAge > 100){
this.catAge = catAge;
return 200;
} else {
return 500;
}
}
public int getCatAge() {
return catAge;
}
public myCatFriend() {
System.out.println("空参构造函数");
}
public myCatFriend(String catName, int catAge) {
System.out.println("有参构造函数");
this.catName = catName;
this.catAge = catAge;
}
}
}

标准JavaBean类
- 类名应具有描述性,需要见面知意、且使用大驼峰命名法(PascalCase)
- 成员变量全部使用
private修饰,并使用小驼峰命名法(camelCase) - 至少提供两个构造方法:
- 无参数的构造方法
- 带全部参数的构造方法。
- 提供合适的成员方法
- 提供每个成员函数的赋值和读取成员方法。
- Getter方法:用于读取成员变量值
- 命名规范:
get + <变量名>(变量名首字母大写) - 布尔类型:
is + <变量名>(变量名首字母大写)
- 命名规范:
- Setter方法:用于设置成员变量值
- 命名规范:
set + <变量名>(变量名首字母大写)
- 命名规范:
- Getter方法:用于读取成员变量值
- 其他必要的成员方法
toString()方法:便于输出对象信息;equals()和hashCode()方法:用于对象比较- 等必要的成员方法...
- 提供每个成员函数的赋值和读取成员方法。
示例:网站用户信息类(符合JavaBean规范)
public class WebUser {
static class WebUserInfo {
private int uid;
private String userName;
private String email;
private char gender;
private int age;
public WebUserInfo() {
}
public WebUserInfo(int uid, String userName, String email, char gender, int age) {
this.uid = uid;
this.userName = userName;
this.email = email;
this.gender = gender;
this.age = age;
}
public int getUid() {
return uid;
}
public void setUid(int uid) {
this.uid = uid;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public char getGender() {
return gender;
}
public void setGender(char gender) {
if (gender == 'M'){
this.gender = 'M';
} else if (gender == 'F') {
this.gender = 'F';
} else {
this.gender = 'N';
}
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
}
对象内存图
Java内存分配
- 栈:方法被加载的时候,方法进栈(方法运行时进入栈,变量也存储在栈中)
- 堆:使用
new关键字创建的对象,会在堆中开辟内存空间并产生地址。 - 元空间:
- 方法区(JDK7及以前):当执行一个Java类的时候,这个类的字节码文件会被加载到方法区临时存储。(JDK7及以前,堆和方法区是同一个内存分配)
- 元空间(JDK8及以后):拆分了原始堆和方法区的功能,现在执行java类的时候,字节码被加载到元空间(Metaspace)中。
- 本地方法栈
- 寄存器
一个对象的内存图
本节文章基本代码
public class Cats {
private String catName;
private int catAge;
public void setCatName(String catName) {
this.catName = catName;
}
public String getCatName() {
return catName;
}
public void setCatAge(int catAge) {
this.catAge = catAge;
}
public int getCatAge() {
return catAge;
}
public Cats() {
}
public Cats(String catName, int catAge) {
this.catName = catName;
this.catAge = catAge;
}
}
创建一个对象的时候:Cats linCat = new Cats();
- 加载class对象:在元空间加载
Cats类。 - 申明局部变量:在栈内存中开辟空间,用于存储
linCat在堆空间的地址。 - 在堆空间中开辟内存空间:堆内存中开辟存储空间,用于存储
linCat成员变量的实际数据、成员方法在元空间(Metaspace)的地址。 - 默认初始化:初始化成员变量的值,如上示例中,
catAge为0(因为是int类型)、catName为null(因为是String类型) - 显示初始化:如果在定义成员变量的时候直接赋值,则进入显示初始化,比如如下的代码,在定义成员变量的同时赋值:
catAge为0修改为3、catName为null修改为laffeyCat。显示初始化发生在默认初始化之后,构造器执行之前。
public class Cats {
private String catName = "laffeyCat"; // 显示初始化
private int catAge = 3; // 显示初始化
}
- 构造方法初始化:调用构造方法。
- 将堆内存中的地址值赋值给左边的对象:将堆内存中的地址赋值给栈内存的
linCat。
创建对象的流程图

单个对象在内存中的内存图

多个对象的内存图
本节文章基本代码
Cats.java
public class Cats {
private String catName;
private int catAge;
public void setCatName(String catName) {
this.catName = catName;
}
public String getCatName() {
return catName;
}
public void setCatAge(int catAge) {
this.catAge = catAge;
}
public int getCatAge() {
return catAge;
}
public Cats() {
}
public Cats(String catName, int catAge) {
this.catName = catName;
this.catAge = catAge;
}
}
MyCats.java
public class MyCats {
public static void main(String[] args) {
Cats linCat = new Cats("lin", 3);
Cats bingCat = new Cats("bing", 4);
}
}
与一个对象的内存区别的地方:
- 对象在堆内存调用元空间的代码:对象在堆内存中存储方法在元空间方法的地址值,多个对象在堆内存指向同一个方法区的内存(因为是使用同样类和方法)
多个同类对象在内存中的内存图

两个变量指向同一个对象的内存图
本节文章基本代码
Cats.java
public class Cats {
private String catName;
private int catAge;
public void setCatName(String catName) {
this.catName = catName;
}
public String getCatName() {
return catName;
}
public void setCatAge(int catAge) {
this.catAge = catAge;
}
public int getCatAge() {
return catAge;
}
public Cats() {
}
public Cats(String catName, int catAge) {
this.catName = catName;
this.catAge = catAge;
}
}

当两个变量指向同一个对象时
- 两个变量的栈内存会同时指向同一个堆内存
public class MyCats {
public static void main(String[] args) {
Cats linCat = new Cats("lin", 3);
Cats bingCat = linCat;
}
}
- 如果对任意一个变量的成员变量修改,则所用引用的变量,均会发生变化。
public class MyCats {
public static void main(String[] args) {
Cats linCat = new Cats("lin", 3);
Cats bingCat = linCat;
System.out.println(linCat.getCatAge());
System.out.println(bingCat.getCatAge());
bingCat.setCatAge(4);
System.out.println(linCat.getCatAge());
System.out.println(bingCat.getCatAge());
}
}

- 如果使用
new关键字修改,则自动断开与原始地址的连接并创建一个新内存空间用于存储新值。
public class MyCats {
public static void main(String[] args) {
Cats linCat = new Cats("lin", 3);
Cats bingCat = linCat;
System.out.println(linCat.getCatAge());
System.out.println(bingCat.getCatAge());
bingCat = new Cats("bing", 4);
System.out.println(linCat.getCatAge());
System.out.println(bingCat.getCatAge());
}
}

this内存原理
this的作用:用于区分局部变量和成员变量。
this的本质:代表方法调用者的地址值。
基本数据类型和引用数据类型的区别
基本数据类型
- 存储在栈内存中,存储的是真实的数据。
- 赋值给其他变量时,赋的是真实的数值
引用数据类型
- 在栈内存中存储堆内存的内存地址,在堆内存中存储真实的数据,在元空间中存储成员方法。
- 赋值给其他变量时,赋的是地址值,修改原始变量或被赋值变量中存储的数据,都会引发数据的更改。
局部变量和成员变量的区别
- 成员变量:类中方法外的变量;
- 局部变量:方法内,方法申明上(即形式参数)
| 特性 | 成员变量 | 局部变量 |
|---|---|---|
| 声明位置 | 类内,方法外 | 方法内或代码块内 |
| 作用域 | 整个类中都可以访问 | 只在声明的方法或代码块内有效 |
| 初始值 | 有默认值 | 必须手动初始化才能使用 |
| 生命周期 | 随着对象的创建而存在,随着对象的销毁而消失。 | 随着方法的调用而存在,随着方法的结束而消失。 |
| 存储位置 | 堆内存(对象的一部分) | 栈内存(存在在方法里) |
| 访问修饰符 | 可以使用public/private/protected |
不能使用访问修饰符 |