Java 面向对象之类和对象
1. 类的定义
- 类:是一组相关属性和行为的集合。可以看成是一类事物的模板,使用事物的属性特征和行为特征来描述该类事物;如:人类
- 实例对象:是一类事物的具体体现。对象是类的一个实例(对象并不是找个女朋友),必然具备该类事物的属性和行为;如:小红、小明、小南
面向过程和面向对象
- 面向过程:当需要实现一个功能时,每个步骤都要亲自实现,详细处理每个细节
- 面向对象:当需要实现一个功能时,不需要关心具体的步骤,而是找一个已经具有该功能的人(或事物)来帮我实现
类定义格式
// 类可以实现对数据的封装,类由成员变量和成员方法组成class className { // 成员变量 成员变量是直接定义在类当中的,在方法外边 // 成员方法 成员方法不要写static关键字}
示例:
class Student { // 成员变量 String name; int age; // 成员方法 public void getName() { System.out.println(this.name); }}
1.1 对象的使用
通常类不能直接使用,需要根据类创建一个对象来使用,使用步骤:
- 导包:
import 包名称.类名称
- 创建对象:
类名称 对象名 = new 类名称()
,如:Student s1 = new Student();
- 使用
对象名.成员变量名对象名.成员方法名(参数)
// 实例化一个对象类名 对象名 = new 类名();对象名.成员变量;对象名.成员方法();
示例:
public class ClassTest { public static void main(String[] args) { Student s1 = new Student(); System.out.println(s1.getName()); // 设置年龄 s1.setAge(18); System.out.println(s1.getAge()); }}class Student { // 成员变量 String name = "rose"; int age; // 成员方法 public String getName() { return this.name; } // 设置年龄 public void setAge(int age) { this.age = age; } // 获取年龄 public int getAge() { return this.age; }}
1.2 成员变量
若成员变量没有进行赋值,那么将会有一个默认值:
数据类型 | 默认值 | |
---|---|---|
基本类型 | 整数( byte,short,int,long ) |
0 |
浮点数( float,double ) |
0.0 | |
字符( char ) |
'\u0000' |
|
布尔( boolean ) |
false |
|
引用类型数组 | 类,接口 | null |
一个类也可以定义多个成员变量和方法,有 public、private
之分:
public
:类外部可以访问private
:私有,类外部不可访问,格式:private 数据类型 变量名;
- 权限修饰符,代表最小权限
- 可以修饰成员变量和成员方法
- 被修饰后的成员变量和成员方法,只能在本类中才能访问
public class ClassTest { public static void main(String[] args) { Person p1 = new Person(); p1.name = "rose"; }}class Person { public String name; public int age;}
以上成员变量 name、age
是公共的,类外部可以访问,这就破坏了类的封装性,导致类外部可以修改,正确的写法应该是:
class Person { // 当外部修改成员变量值的时候,会编译报错 p1.name = "rose"; private String name; private int age;}
1.3 成员变量和局部变量
- 成员变量
- 位置:在方法外部,直接写在类当中
- 作用范围:整个类全都可以通用
- 默认值:有默认值
- 内存位置:位于堆内存
- 生命周期:随着对象创建而诞生,随着对象被垃圾回收而消失
- 成员变量
- 位置:在方法内部
- 作用范围:只有方法当中才可以使用,出了方法就不能再用
- 默认值:无默认值
- 内存位置:位于栈内存
- 生命周期:随着方法进栈而诞生,随着方法出栈而消失
示例:
class Person { public String name; // 成员变量 public int age; public void setName(String name) { String gender = "男"; // 局部变量 this.name = name; }
2. 方法
2.1 private 修饰符
在类外部可以通过对象来修改成员变量,但是却 无法阻止不合理的数据 ,比如: age
有可能被设置为负数,解决方案:
private
关键字修饰要修改的成员变量, 被修饰后的成员变量和成员方法,只能在本类中才能访问- 再通过
Getter/Setter
方法实现外部也可以访问和修改成员变量,同时也可以校验数据的合理性 - 那么可以通过
private
关键字和Getter/Setter
方法来实现间接修改,这样也可以在Getter/Setter
方法中校验数据的合理性
public class ClassTest { public static void main(String[] args) { Person p1 = new Person(); p1.setName("rose"); System.out.println(p1.getName()); }}class Person { private String name; private int age; private boolean male; // bool 类型成员变量 // 设置名字 public void setName(String name) { this.name = name; } // 访问名字 public String getName() { return this.name; } public void setMale(boolean b) { male = b; } // bool 类型 Getter 方式是 isXXX() public boolean isMale() { return male; }}
注意: bool
类型 Getter
方式是 isXXX()
,而非 Getter()
2.2 方法定义
语法格式:
修饰符 方法返回类型 方法名(方法参数列表) { 若干方法语句; return 方法返回值;}
-
修饰符:
public、private
- 方法返回类型:
String、int、void
等
注意:若没有返回值,可设置返回类型为 void
,可省略 return
2.3 private 方法和 this 变量
有 public
方法就有 private
方法,同理它不允许外部调用,只能在类内部调用:
// private 方法private int calcAge(int currentYear) { return currentYear - this.age;}
this 变量
this
代表所在类的当前对象的引用(地址值),即对象自己的引用,可解决 成员变量和局部变量、参数的命名冲突 ,如:成员变量和局部变量名字一致
使用格式:
this.成员变量
示例:
// 成员变量和 setName 的参数名冲突,this.name 表示调用成员变量 nameclass Person { private String name; // 设置名字 public void setName(String name) { this.name = name; } // 访问名字 public String getName() { return this.name; }}
若没有命名冲突可省略 this
,但是最好是显示地写上。
注意 :方法被哪个对象调用,方法中的 this
就代表那个对象。即谁在调用, this
就代表谁。
2.4 方法参数
方法可以接收 0 个或任意个参数,定义参数时需要数据类型,传递参数必须严格按照参数数据类型传递:
class Person { public String name; public int age; // 执行需传入的参数必须为 String 类型 public void setName(String name) { this.name = name; } // 无参数 public String getName() { return this.name; }}// 参数传递Person p1 = new Person();p1.setName("rose");
可变参数
当传入的参数不固定或不确定数目时,可使用可变参数:
// 格式 类型...class Person { public String[] names; // 接收的是一个 String 数组 public void setName(String... names) { this.names = names; }}// 参数传递Person p1 = new Person();p1.setName();p1.setName("rose");p1.setName("rose", "lila");
2.5 参数绑定
- 基本类型参数的传递:是调用方 值的复制 。双方各自的后续修改,互不影响
- 引用类型参数的传递:调用方的变量,和接收方的参数变量, 指向的是同一个对象 。双方任意一方对这个对象的修改,都会影响对方
- 注意 : String的值是不可变的 ,对 String 重新赋值的时候,会重新创建一个变量的引用;
2.5.1 基本类型参数传递
public class ClassTest { public static void main(String[] args) { Person p1 = new Person(); int n = 20; p1.setAge(n); System.out.println(p1.getAge()); // 20 n = 18; System.out.println(p1.getAge()); // 20 }}class Person { private int age; public int getAge() { return this.age; } public void setAge(int age) { this.age = age; }}
修改外部的局部变量 n
,不影响实例 p
的 age
字段,原因是 setAge()
方法获得的参数,复制了 n
的值,因此, p.age
和局部变量 n
互不影响。
2.5.2 引用类型参数传递
public class ClassTest { public static void main(String[] args) {// Person p1 = new Person();// int n = 20;// p1.setAge(n);// System.out.println(p1.getAge()); // 20// n = 18;// System.out.println(p1.getAge()); // 20 Person p2 = new Person(); String[] full_name = {"li", "xiaoer"}; p2.setName(full_name); System.out.println(p2.getName()); // li xiaoer full_name[1] = "da"; System.out.println(p2.getName()); // li da } }class Person { private int age; private String[] names; public int getAge() { return this.age; } public void setAge(int age) { this.age = age; } public String getName() { return this.names[0] + " " + this.names[1]; } public void setName(String[] names) { this.names = names; }}
数组 full_name
第 1 个元素修改, getName()
方法获取的 names
也跟着改变,这是因为两者指向的是同一变量,其内存地址是同一个。
2.5.3 引用类型之 String
类型参数传递(绑定)
public class ClassTest { public static void main(String[] args) { Person p2 = new Person(); String n1 = "rose"; p2.setName(n1); System.out.println(p2.getName()); // rose n1 = "lila"; System.out.println(p2.getName()); // rose } }class Person { private int age; private String name; public int getAge() { return this.age; } public void setAge(int age) { this.age = age; } public String getName() { return this.name; } public void setName(String name) { this.name = name; }}
上面说引用类型的参数传递,调用方的变量,和接收方的参数变量, 指向的是同一个对象 。双方任意一方对这个对象的修改,都会影响对方;但是 String
类型是个特例。
String的值是不可变的 ,对String 重新赋值的时候,会重新创建一个新的变量的引用;所有当变量 n1 = "lila";
时,它会创建一个新的变量的引用,而 setName()
方法时获取的原先的变量引用,所以修改不会改变。
3. 构造方法
构造方法:可以实现在创建实例的时候, 初始化实例字段 ,特性:
-
构造方法名称即为类名,无返回值,无
void
- 调用构造方法,必须用
new
操作符 - 可以有多个构造方法(有参数、无参数、不同参数), 若没有定义构造方法,编译器会自动为我们生成一个默认构造方法,它没有参数 ,也没有执行语句
public class StructMethod { public static void main(String[] args) { Person1 p = new Person1("rose", 18); System.out.println(p.getAge()); System.out.println(p.getName()); Person1 p2 = new Person1("lila"); Person1 p3 = new Person1(); }}class Person1 { private String name; private int age; // 默认构造方法 public Person1() { } // 自定义构造方法 1 public Person1(String name, int age) { this.name = name; this.age = age; } // 自定义构造方法 2 public Person1(String name) { this.name = name; } public String getName() { return this.name; } public int getAge() { return this.age; }}
多个构造方法调用:
Person1 p = new Person1("rose", 18);Person1 p2 = new Person1("lila");Person1 p3 = new Person1();
一个构造方法调用另一个构造方法:
this("john", 19); // 便于代码复用
4. 方法重载
在 Java
类中可以定义 多个名称相同的方法 ,若只是参数不同,功能类似,那么可以将这类方法称为同名方法,也可以叫做 方法重载 ,一般而言这类方法 返回类型应该相同 :
class Book { 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) { System.out.println("Hello " + name + "You're age " + age); }}
方法重载的目的是,功能类似的方法使用同一名字,更容易记住,调用起来更简单,如 String
类提供了多个重载方法 indexOf()
5. Java 对象内存图
【JavaSE】Java面向对象的思想(类、对象、对象调用内存图)
一个 Java
对象的创建 --> 使用在内存中总共大概会开辟三块内存区域:栈内存 Stack
、堆内存 Heap
、方法区 Method Area
- 方法调用的时候,该方法所需的内存空间在 栈内存 中分配,这个过程叫 压栈 ,方法结束后,该方法所属的内存空间释放,成为 出栈
- 栈中主要存储的是方法体当中的局部变量
- 方法的代码段以及整个类的代码段都被存储到方法区内存当中,在类加载的时候这些代码片会载入
- 程序执行
new
创建对象时,存储在 堆内存 当中,对象内部的实例变量
1、只有一个对象的内存图
- 方法区:总共有两个类
Phone、Demo1PhoneOne
,方法有:main、call、send
- 栈内存:当执行主函数时,会在栈内存开辟一块空间;当使用对象去修改或访问成员变量时,会直接在堆内存中找到成员变量进行修改,而当调用成员方法时,通过堆内存中的 成员方法内存地址 找到相应的成员方法,此时成员方法会从方法区进入栈空间(压栈),执行完毕后,就立马出栈
- 堆内存:当
new
创建对象时,会在堆内存开辟一块空间
2、两个对象使用同一个方法的内存图
创建两个对象情况和一个对象类似:
- 创建第二个对象是在堆内存中新建一个对象
new Phone();
赋给第二个对象名two
的地址也是一个新的地址值 - 对象访问成员变量,访问的是该对象自己的成员变量。
- 两个对象使用同一个方法,会根据地址值指向同一个方法。因为两个对象中的成员方法,保存的都是方法区中
Phone.class
中对应成员方法的地址值 - 两个对象访问成员变量,调用成员方法不会产生任何关联
3、两个引用指向同一个对象的内存图
-
Phone two =one;
是将one
中保存的对象地址值赋给的two
,two
也指向了该对象。 - 根据
one
和two
的地址值都可以访问到该同一对象的成员变量、成员方法