类的定义
在面向对象编程中,类(Class)是一种定义对象属性(变量)和行为(方法)的蓝图或模板。类是创建对象的基础,它定义了对象的状态(属性)和行为(方法)。每个对象又具有独特的标识、状态、行为。
状态(属性):又叫做末特征,或者属性。使用数据域进行表示。java中使用变量定义数据域。
行为(方法):又叫动作。调用对象一个方法就是要求对象完成一个动作。在java中,使用方法来定义动作。
在现实世界中,“学生”就是抽离出来的一个类,而一些具体的学生名字“张三、李四”等就是一个个实例。
一个对象是类的一个实例。可以从一个类中创建多个实例。创建实例的过程称为实例化。
package cdtestc2020;
/**
* 每个对象都具有自己独特的标识、状态和行为
* 状态也称为属性或者特征。用数据域(成员变量)描述
* 行为也称为动作。通过成员方法描述
* 使用类描述同一类型的对象
* 类是对象模板、蓝本或者说是合约。用来定义对象的数据域是什么以及方法是做什么的
*/
public class Circle {
//通过数据域描述对象的属性(状态)
public double radius;
/**
* 构造方法在使用new关键字创建对象时被调用
* 构造方法作用:一般用作对象的初始化
* 构造方法是一种特殊的方法:
* 1、构造必须具有和所在类形同的名字(必须和类名相同)
* 2、构造方法没有返回值类型,连void都没有
* 3、使用new创建对象时会调用构造方法,作用为初始化对象
*
* 构造方法也可以进行重载
*
* 如果类中没有定义构造方法,编译器会自动添加一个公共的无参的构造方法(默认),如果已经定义了构造方法,编译器不再添加
* 一般构造定义在数据域下
*/
public Circle() {
System.out.println("调用无参构造方法");
radius = 1;
}
public Circle(double newRadius) {
System.out.println("初始化圆的半径为:" + newRadius);
radius = newRadius;
}
/**
* 成员方法描述对象的行为:动作(计算圆的面积)
* @return 圆的面积
*/
public double getArea() {
return 3.14 * radius * radius;
}
/**
* 对象的行为/动作
* 调用一个对象的方法就是要求对象完成一个动作
* @return 该圆的周长
*/
public double getPerimeter() {
return 3.14 * 2 * radius;
}
}
而instance是对象实例,instance是根据class创建的实例,可以创建多个instance,每个instance类型相同,但是各自的属性可能不同。
package cduestc2020;
public class CircleTest {
public static void main(String[] args) {
Circle circle1 = new Circle();//调用的对应的构造方法
double perimeter1 = circle1.getPerimeter();
System.out.println(circle1 + "==>radius:" + circle1.radius + "==>area:" + circle1.getArea() + "==>perimeter:" + perimeter1);
Circle circle2 = new Circle(25);
System.out.println(circle2 + "==>radius:" + circle2.radius + "==>area:" + circle2.getArea() + "==>perimeter:" + circle2.getPerimeter());
Circle circle3 = new Circle(125);//调用的对应的构造方法
System.out.println(circle3 + "==>radius:" + circle3.radius + "==>area:" + circle3.getArea() + "==>perimeter:" + circle3.getPerimeter());
}
}
要想使用对象,就必须先构造对象并指定其初始状态,可以通过使用new操作符从构造方法中创建一个对象。
在以上的案例中,数据域(成员变量)redius称为实例变量。因为它依赖于某个具体的实例,同样的,像getArea()以及getPerimeter()等方法被称为实例方法。
成员方法
又叫做实例方法,需要在调用之前创建其类的对象的方法。
因为我们在实例上要使用它。调用对象上的实例方法的过程就叫做调用对象。
构造方法
用来初始化对象的方法。
使用构造方法构造对象的几点注意事项:
- 构造方法在使用new操作符创建对象时被调用。
- 构造方法必须具备和所在类同名。
- 构造方法没有返回值类型,不需要使用void修饰符。
- 构造方法的作用就是初始化对象。
构造方法也可以进行方法重载(也就是说可以有多个同名构造方法,但是他们的参数不一样),这样就更利于用不同的初始数据值来构造对象。一个类可以没有构造方法,在这种情况下,编译器会自动添加一个方法为空的公公共无参数构造方法,这个构造方法被称为默认构造方法。当且仅当类中没有明确定义任何的构造方法时才会自动提供它。
public class Person {
// 成员变量
private String name;
private int age;
// 构造方法
public Person(String name, int age) {
this.name = name;
this.age = age;
}
// 成员方法
public void introduce() {
System.out.println("Hello, my name is " + name + " and I am " + age + " years old.");
}
}
//在以上的例子中,person类中有一个构造方法person,它接受两个参数:age 、name。
//在这个构造方法中,我们可以使用this关键字来区别成员变量和构造方法的参数。
既然有了构造方法,那么怎么使用这个构造方法呢?
public class Main {
public static void main(String[] args) {
// 使用构造方法创建Person对象
Person person = new Person("Alice", 30);
// 调用成员方法
person.introduce();
}
}
//首先要创建一个对象,即初始化对象。传入参数。
//再使用introduce方法来打印出这个对象的自我介绍。
成员方法(实例方法)和构造方法的区别:
方法名称不同
- 构造方法在创建对象时自动调用,不需要显式调用。
- 成员方法需要显式调用,通常通过【对象名.方法名】的方式调用。
调用方式不同
- 构造方法在创建对象时被调用,用于创建对象时初始化成员变量。
- 成员方法用于操作对象的状态或执行特定的任务。
用途不同
- 构造方法用于初始化对象的状态,通常包含对成员变量的赋值操作。
- 成员方法用于执行特定的操作,如计算、数据处理、逻辑判断等。
方法体不同
- 构造方法没有返回值,甚至连void也没有。
- 成员方法可以有返回值,也可以没有返回值(有viod)。
返回类型不同
- 成员方法的名称可以任意,只要它符合Java的命名规则
- 构造方法的名称必须与类名完全相同。
静态变量staitc
静态变量又叫类变量,静态变量将变量值存储在一个公共的内存地址,被类中的所有对象所共享。
静态常量 static final
类中的常量是被该类的所有对象所共享的。因此,常量应该声明为final static,例如,Math类中的常量PI是如下定义的:
pulbic final static double PI = 3.1415926538979323846;
静态方法
不需要创建类对象就可以调用的方法。
在下面两种情况下可以使用静态方法:
- 方法不需要访问对象状态,因为它需要的所有参数都通过显式参数提供(例如:Math.pow)。
- 方法只需要访问类的静态字段(例如:Student.getNextId)。
访问修饰符
public : 任何其他类都可以访问使用该修饰符的类、方法或变量。最宽松的访问级别。
default(没有修饰符):在同一个包内的其他类可以访问使用该修饰符的类、方法或变量。这是默认的访问级别,当没有指定任何访问修饰符时,就隐含了default访问级别。
protected :允许子类访问父类中的数据域或方法,但不允许非子类访问这些数据域和方法。
private : 限定方法和数据域只能在自己的类中访问,即只能被本类访问。
数据域封装
为了避免对数据域的直接修改,应该使用private修饰符将数据域声明为私有的,这称为数据域封装。在定义私有数据域的类外的对象是不能访问这个数据域的。
为了能够访问私有数据域,可以提供一个访问器返回数据域的值(get方法)。为了更新一个数据域,可以提供一个修改器给数据域设置新值(set方法)。
【当一个类的数据域被声明为private时,这意味着这些域只能在类的内部被访问和修改。为了允许外部代码安全地访问和修改这些私有数据域,通常会提供公共的访问器(getter)和修改器(setter)方法。】
封装通常通过以下方式实现:
- 使用private访问修饰符来声明数据域,这样只有类内部的方法可以访问它们。
- 提供公共的getter和setter方法来访问和修改数据域的值。
public class Student {
/**
* 学生id,用于唯一标识该学生
*/
private int id;
/**
* 下一个id号
*/
private static int nextId;
/**
* 学生姓名
*/
private String name;
{
id = nextId++;
}
public Student() {
}
public Student(String name) {
this.name = name;
}
/**
* 实例域id的访问器
* @return id实例域值
*/
public int getId() {
return id;
}
/**
* 实例域id修改器
* @param id id值
*/
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public static int getNextId() {
return nextId;
}
}
向方法传递对象参数
在java中,可以向方法传递对象作为参数。当对象作为参数传递时,实际上传递的是对象的引用(reference),而不是对象的实际副本。这意味着方法内部可以修改原始对象的状态。
下面是一个实例:
public class Student {
private String name;
private int age;
// 构造方法
public Student(String name, int age) {
this.name = name;
this.age = age;
}
// getter和setter方法
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
// 打印学生信息的方法
public void printStudentInfo() {
System.out.println("Name: " + name + ", Age: " + age);
}
}
//测试类main
public class Main {
public static void main(String[] args) {
// 创建一个Student对象
Student student = new Student("Alice", 20);
// 调用方法,传递Student对象作为参数
printStudentInfo(student);
// 打印原始对象的信息,以验证方法内部是否修改了状态
System.out.println("After method call:");
student.printStudentInfo();
}
// 接收Student对象作为参数的方法
public static void printStudentInfo(Student student) {
// 修改对象的状态
student.setName("Bob");
student.setAge(21);
// 打印修改后的信息
student.printStudentInfo();
}
}
在以上的例子中,在这个例子中,Student类定义了一个printStudentInfo方法,用于打印学生的信息。
在Main类中,我们创建了一个Student对象student,然后调用了printStudentInfo方法,并将student对象作为参数传递。在printStudentInfo方法中,我们修改了传递进来的Student对象的状态,将名字改为"Bob",年龄改为21。
然后,我们调用了student对象的printStudentInfo方法来打印修改后的信息。由于对象是通过引用传递的,所以原始对象student的状态在方法调用后确实被修改了。这就是为什么在方法内部修改对象状态(属性)时,需要小心处理,以避免意外的副作用。
针对基本数据类型,传递的是基本数据类型值。
针对引用数据类型,传递的是引用。
不可变对象和类
通常情况下,对象创建之后其内容是可以改变的。但是有时候我们需要对象一旦创建之后其内容不能被更改,称这种对象就叫做不可变对象,它的类就是不可变类。
比如String类就是不可变的。要想使一个对象成为不可变对象,需要满足以下条件:
- 所有数据域都是私有的。
- 没有修改器方法。(setter)
- 没有一个返回指向可变数据域的引用的访问器方法。
import java.util.Date;
public class Employee {
private static int nextId;
private int id;
private String name;
private Date hireDate;
public Employee(String name) {
this.id = nextId;
nextId++;
this.name = name;
this.hireDate = new Date();
}
public int getId() {
return id;
}
public String getName() {
return name;
}
public Date getHireDate() {
//返回的是Date对象的一个引用。通过这个引用可以改变hireDate的值
//return hireDate;
//可以修改为:
return (Date)hireDate.clone();
}
}
成员变量和局部变量
共同点:
- 都是变量,他们的定义形式相同:【类型】 【变量名】 = 【初始化值】;
- 都有作用域:作用域是在一对大括号内
不同点:
内存中存放的位置不同
- 成员变量存放在堆空间内
- 局部变量存放在栈空间内
声明的位置不同(作用域不同)
- 成员变量声明在类的内部,方法的外部,作用整个类;
- 局部变量声明在方法的内部,从它声明的地方开始到包含它最近的块结束。
初始化值不同
- 成员变量可以不赋初值,其默认值按照其数据类型来定
- 局部变量必须显式地赋初值
权限修饰符不同
- 成员变量的权限修饰符有四个:public (default) protected private
- 局部变量没有权限修饰符,其访问权限依据其所在的方法而定(与方法的访问权限相同)。
用var声明局部变量
在Java 10中,如果可以从变量的初始值推导出它们的类型,那么可以用var关键字声明局部变量,而无须指定类型。例如,可以不这样声明:
Circle circle = new Circle(10);
只需要写以下代码:
var circle = new Circle(10);
这一点很好,因为这样可以避免重复写类型名Circle。
this关键字
接下来我们用一个例子来说明一下:
public class Account {
private String name;
private double balance;
private String pwd;
//Account类的一个构造器
public Account (String name,double balance,String pwd){
//构造器的实现---初始化对象
this.name = name;
this.balance = balance;
this.pwd = pwd;
}
}
假如我们不按照上面的代码进行操作,如果我们不使用this关键字会怎么样???????
class Account {
private String name;
private double balance;
private String pwd;
//Account类的一个构造器
public Account(String name, double balance, String pwd) {
//构造器的实现---初始化对象
//不用this
name = name;
balance = balance;
pwd = pwd;
}
public void showInfo() {
System.out.println("name:" + name + " " + "balance:" + balance + " " + "pwd:" + pwd);
return;
}
}
public class Main {
public static void main(String[] args) {
Account account = new Account("Yaoyao", 20, "123456");
account.showInfo();
}
}
其运行结果如下:
解释一下,因为我们错误的认为 name = name 左边的name是成员变量,但是实际上等号左右都是局部变量,因此此句代码就是将局部变量的值赋给局部变量。然而成员变量的值忆旧没有发生改变,仍然是默认值。
因此我们使用this.属性区别成员变量和局部变量。
那么this究竟是什么呢?
- this可以理解为一个对象的属性(成员变量),但是这个属性是隐藏的。
- 和其他对象的属性一样,在进行new创建对象时,会在堆为对象分配空间,而属性就是存储在这个空间中,且this属性的值也存储在这个空间内存地址中。即this指向该对象。
综上所述。this是对象的隐藏属性,也就是一个普通的成员变量。在创建对象时会为每个新对象分配该对象的专属成员变量(this就是其中的一个),this这个成员变量就存储在堆内存中。