[03] 类的结构和创建对象
1、类的结构和定义
先回顾一下,类的基本概念:
- 类定义了对象共同的属性和行为(即方法)
- 具体的某个对象需要通过类来进行申明
那么显而易见地,类中应该包含的东西就有,属性、方法,另外,既然对象要通过类来声明,那么类中必须要有一个类似生产机器的东西来生成对象,这个类似生产机器的东西叫做构造函数。另外,类还可有内部类和块,这两种使用较少。
所以,类的结构内容就是:
- 属性(对象数据的描述)
- 方法(对象的行为)
- 构造函数(用于生产对象,也叫做实例化对象)
- 内部类(在类体中申明的类)
- 块(分为静态块、实例块)
public class Person {
//属性
String name;
int age;
//方法
public void eat() {
System.out.println("吃东西");
}
public void run() {
System.out.println("跑起来");
}
//构造方法
public Person() {
}
//块
{
System.out.println("我是实例块");
}
static {
System.out.println("我是静态块");
}
//内部类
class InnerClass{
}
}
33
1
public class Person {
2
//属性
3
String name;
4
int age;
5
6
//方法
7
public void eat() {
8
System.out.println("吃东西");
9
}
10
11
public void run() {
12
System.out.println("跑起来");
13
}
14
15
//构造方法
16
public Person() {
17
18
}
19
20
//块
21
{
22
System.out.println("我是实例块");
23
}
24
25
static {
26
System.out.println("我是静态块");
27
}
28
29
//内部类
30
class InnerClass{
31
32
}
33
}
1.1 类的申明
【访问权限修饰符】【修饰符】class 类名 {
类体
}
//【】中的内容表示可选项
e.g.
public class Person {
...
}
9
1
【访问权限修饰符】【修饰符】class 类名 {
2
类体
3
}
4
5
//【】中的内容表示可选项
6
e.g.
7
public class Person {
8
...
9
}
- 类的命名规范:首字母大写,之后的单词首字母才大写,其余小写
- 访问权限修饰符:default、private、protected、public(default即什么都不写)
- 修饰符:final、synchronized、abstract
- 访问权限修饰符和修饰符在之后说明,本章不做说明
1.2 属性的申明
【访问权限修饰符】【修饰符】数据类型 属性名【=初值】;
//【】中的内容表示可选项
e.g.
private String name;
private double salary;
private static int count = 0;
7
1
【访问权限修饰符】【修饰符】数据类型 属性名【=初值】;
2
3
//【】中的内容表示可选项
4
e.g.
5
private String name;
6
private double salary;
7
private static int count = 0;
- 属性的命名规则:小写,多个单词则后面的单词首字母大写
- 注意:属性又称为成员变量,是声明在类中的变量,方法体中声明的变量称为局部变量或临时变量
1.3 方法
1.3.1 方法的申明
【访问权限修饰符】【修饰符】返回值数据类型 方法名(形式参数列表) {
执行语句;
return 返回值;
}
//【】中的内容表示可选项
e.g.
public String eat(String food){
return "吃" + food;
}
public void eat(){
System.out.println("吃东西");
}
14
1
【访问权限修饰符】【修饰符】返回值数据类型 方法名(形式参数列表) {
2
执行语句;
3
return 返回值;
4
}
5
6
//【】中的内容表示可选项
7
e.g.
8
public String eat(String food){
9
return "吃" + food;
10
}
11
12
public void eat(){
13
System.out.println("吃东西");
14
}
- 方法命名规范:小写,多个单词则后面的单词首字母大写
- 返回值类型必须填写,如果没有类型也需要填写void(如果是void则不需要return语句)
1.3.2 方法中的部分概念解释
方法,其实就是定义在类中具有特定功能的一段独立小程序,更多时候我们称之为函数。
形式参数,就是一个变量,用来传递给方法进行使用,就像 public String eat(String food){ ... },某个人要执行吃的行为,那得递给他食物他才能行动呀,所以这里的食物,就是吃这个方法的参数。
返回值,即如果你关心并希望得到这个方法的执行结果,那么需要规定返回值的数据类型,再使用return语句把结果告诉你。如果你只是进行一些操作,不关心最终的结果,就像你只想执行吃饭的操作,你不关心最终吃没吃饱,那么就意味着没有返回值,那么返回值数据类型就填写void,也没有return语句。
1.3.3 方法的特点
- 定义方法可以将功能代码进行封装
- 方法可以复用
- 方法只有被调用时才会执行
- 方法中可以调用其它方法,但是不可能在方法内部定义方法
1.3.4 方法重载
如果在一个类中,需要实现两个整数求和,两个浮点数求和,这两个方法如何定义?
1、定义两个方法,分别命名为addInt、addDouble
2、定义一个方法,叫add,但形参数据类型不同
显然,使用后者更为适宜,所以在Java中,如果有多个同名但是不同参数的方法,我们称之为“方法重载”,编译器会根据调用时传递的实际参数自动判断具体调用的是哪个重载方法。
e.g.
int add(int x, int y){...}
float add(float x, float y){...}
double add(double x, double y){...}
5
1
e.g.
2
3
int add(int x, int y){...}
4
float add(float x, float y){...}
5
double add(double x, double y){...}
所谓的不同参数,是指参数的:
- 数量不同
- 类型不同
- 顺序不同
值得一提的是,方法重载跟方法的返回值类型没有任何关系,也就是说,如果只是返回值不同的方法,不能构成重载。
//错误的方法重载
void fun(int x){...}
int fun(int x){...}
3
1
//错误的方法重载
2
void fun(int x){...}
3
int fun(int x){...}
1.4 构造方法
【访问权限修饰符】类名(形式参数列表){
方法体
}
//【】中的内容表示可选项
4
1
【访问权限修饰符】类名(形式参数列表){
2
方法体
3
}
4
//【】中的内容表示可选项
- 构造方法的名称必须和类名相同
- 构造方法也有方法重载
- 构造方法中也可以编写复杂程序,但是一般只用于定义属性,即初始化对象属性
那么问题来了:
构造方法是用来声明类的,那么为什么我们有时候不写构造方法,依然可以进行实例化?
那么答案来了:
任何类都有一个默认的无参的构造方法,只有在你自定义了某个构造方法后,这个默认的无参构造方法才会失效。
2、创建对象
2.1 实例化对象
使用new调用构造方法,创建类的一个对象:
//调用无参数的构造方法
类名 对象名 = new 该类的构造方法();
e.g. Book javaBook = new Book();
//调用有参数的构造方法
类名 对象名 = new 该类的构造方法(参数1, 参数2...);
e.g. Book javaBook = new Book("西游记");
7
1
//调用无参数的构造方法
2
类名 对象名 = new 该类的构造方法();
3
e.g. Book javaBook = new Book();
4
5
//调用有参数的构造方法
6
类名 对象名 = new 该类的构造方法(参数1, 参数2...);
7
e.g. Book javaBook = new Book("西游记");
使用对象,可以使用 “.” 进行以下操作:
- 访问类的属性:对象名.属性
- 调用类的方法:对象名.方法名()
e.g.
javaBook.name = "东游记";
javaBook.borrow();
3
1
e.g.
2
javaBook.name = "东游记";
3
javaBook.borrow();
2.2 实例化对象的过程
假设存在一个类Person,创建对象语句如下:
Person zhangsan = new Person();
1
1
Person zhangsan = new Person();
将上面的语句分为两个步骤:
Person zhangsan;
zhangsan = new Person();
2
1
Person zhangsan;
2
zhangsan = new Person();
即,实例化一个对象后,其实有两个实体:
- 对象引用变量,在栈空间,即zhangsan,称之为引用,或句柄,存放的是对象的虚地址
- 对象本身,即使用new Person()在堆空间里创建的实体,将对象的属性值存储在堆里
- 可以使用引用变量操作对象的属性和方法
意思类似于旅馆的房间和钥匙:
- 当你使用new时,就相当于在旅馆前台说你要开个房间,那么前台会给你一把钥匙,就是这里的引用变量,如上例的zhangsan
- 钥匙指向房间,所以有了钥匙你就能找到房间,才能对房间进行布置
public class Room {
private String bed;
private String lamp;
public Room(String bed, String lamp) {
this.bed = bed;
this.lamp = lamp;
}
public String getBed() {
return bed;
}
public void setBed(String bed) {
this.bed = bed;
}
public String getLamp() {
return lamp;
}
public void setLamp(String lamp) {
this.lamp = lamp;
}
public static void main(String[] args) {
Room room1 = new Room("单人大床", "小灯");
Room room2 = new Room("双人大床", "大灯");
}
}
x
1
public class Room {
2
3
private String bed;
4
private String lamp;
5
6
public Room(String bed, String lamp) {
7
this.bed = bed;
8
this.lamp = lamp;
9
}
10
11
public String getBed() {
12
return bed;
13
}
14
15
public void setBed(String bed) {
16
this.bed = bed;
17
}
18
19
public String getLamp() {
20
return lamp;
21
}
22
23
public void setLamp(String lamp) {
24
this.lamp = lamp;
25
}
26
27
public static void main(String[] args) {
28
Room room1 = new Room("单人大床", "小灯");
29
Room room2 = new Room("双人大床", "大灯");
30
}
31
32
}
如上例:
1、把Room类的字节码加载到内存
2、根据Room类的申明,把属性bed、lamp初始化到内存堆,并赋默认值,String的默认值为null
3、根据代码,调用两个参数的构造方法,把"单人大床"和"小灯"分别赋值给bed和lamp
4、在栈中创建一个变量room1,用来引用堆中的对象,room1的值是一个16进制的数,表示一个虚地址
5、重复2-4创建room2,room2和room1是不同的引用,指向不同的具体对象
3、块与内部类
块在类中申明,类似一个没有方法申明的方法体,分实例块和静态块。
3.1 块
3.1.1 实例块
顾名思义,属于某个具体的对象,所以会在每次调用构造方法之前自动调用
{
//任何符合语法的Java代码
}
3
1
{
2
//任何符合语法的Java代码
3
}
3.1.2 静态块
在类加载时被调用一次,仅一次,与是否创建对象无关
static {
//任何符合语法的Java代码
}
3
1
static {
2
//任何符合语法的Java代码
3
}
3.2 内部类
将一个类的定义放到另一个类中,就是内部类。
public class Outer{
private int x;
class Inner{
public test(){
x++;
}
}
}
1
public class Outer{
2
private int x;
3
4
class Inner{
5
public test(){
6
x++;
7
}
8
}
9
}
内部类对于初学者使用很少,这里不再详细阐述了,可以参考: