面向对象
面向对象的思想
- 封装性
- 封装是面向对象的核心思想。具有两层含义:
- 把对象的属性和行为看成一个密不可分的整体,将两者组合在一起,即封装在对象中。
- 信息隐藏,将不想让外界知道的信息隐藏起来
- 继承性
- 继承性主要是描述类与类之间的关系。通过继承,可以在原有类的基础上对功能进行扩展。例如有一个汽车类的普通特性和功能。进一步再生产轿车类,而轿车类中不仅应该包含汽车类的特性和功能,还应该增加轿车特有的功能,这时,可以让轿车类继承汽车类,在轿车类中单独添加其独有的特性和方法就可以了。继承不仅增强了代码的复用性,提高了开发效率,还能降低程序产生错误的可能性,为程序的维护以及扩展提供了便利。
- 多态性
- 多态性是指在一个类中定义的属性和方法被其他类继承后,他们可以具有不同的数据类型或表现出不同行为,这使得同一个属性和方法在不同类中具有不同语义。例如,汽车和飞机同样是交通工具,汽车在陆地上行驶,而飞机在天空中飞行,所以不同的对象表现的行为是不一样的。多态的特性使程序更抽象、便捷,有助于开发人员设计程序时分组协同开发。
类与对象
在面向对象技术中,为了做到让程序对事物的描述与事物在现实中的形态保持一致,提出了两个概念:类和对象
在java中类和对象是最基本、最重要的单元。
- 类表示某类群体的一些基本特征抽象
- 对象 表示一个个具体的事物
例如,在现实生活中,学生这个群体就可以表示为一个类,而某个具体的学生就可以称为对象。一个具体的学生有自己的姓名和年龄等信息,这些信息在面向对象的概念中称为属性;学生可以看书和打篮球,看书和打篮球这些行为在类中就可以称为方法。
总结来说: - 类用于描述多个对象的共同特征。是对象的模板。
- 对象用于描述现实中的个体,是类的实例。
对象是根据类创建的,一个类可以对应多个对象。
类的定义
在面向对象的思想中最核心的就是对象,创建对象的前提是定义一个类。类是java中一个重要的引用数据类型,也是组成java程序的基本要素,所有的java程序都是基于类的。
类是对象的抽象,用于描述一组对象的共同特征和行为。类中可以定义成员变量和成员方法。成员变量用于描述对象的特征,成员变量也被称作对象的属性;成员方法用于描述对象的行为,可简化为方法。
类的定义格式如下:
class 类名{
成员变量;
成员方法;
}
根据上述格式定义Student类,成员变量包括name,age,sex;成员方法包括read().
class Student{
String name; //声明String类型的变量name
int age; //声明int类型的变量age
String sex; //声明String类型的变量sex
void read(){
System.out.println("大家好,我是"+name+",我在看书");
}
}
以上代码中定义了String类。其中Student是类名,name,age,sex是成员变量,read()是成员方法,在成员方法read()中可以直接访问成员变量name,
注意一点:
局部变量与成员变量的不同在java中,定义在类中的变量称为成员变量,定义在方法中的变量称为局部变量,如果在某个方法中定义的局部变量与成员变量同名,这种情况是允许的,此时,在方法中通过变量名访问的局部变量,而非成员变量
class Student{
int age = 30;
void read(){
int age = 50;
System.out.println("大家好,我"+age+"岁,我在看书。");
}
}
上述代码中,在Student类的read()方法中有一条打印语句,打印了变量age, 此时打印的是局部变量age,也就是说当另一个程序调用read()方法时,输出的age值为50,而不是30。
对象的创建与使用
上面我们定义了Student类,想要使用一个类,则必须创建该类的对象。在java程序中可以使用new关键字创建对象,使用new关键字创建对象的具体格式:
类名 对象名 = null;
对象名 = new 类名();
上述格式中,创建对象分为声明对象和实例化对象两步。也可以直接通过下面的方式创建对象:
类名 对象名 = new 类名();
例如,创建Student类的实例对象,示例代码如下:
Student stu = new Student();
上述代码中,
- new Student()用于创建Student类的一个示例对象(称为Student对象)
- Student stu声明了一个Student类的变量stu。
- 运算符= 将新创建的实例对象地址赋值给变量stu
- 变量stu 引用的对象简称为stu对象。
class Student{
String name;
void read(){
System.out.println("大家好,我是"+name+",我在看书");
}
}
public class Test{
public static void main(String[] args){
Student stu = new Student();
}
}
上述代码在main()方法中实例化了Student对象,对象名为stu。使用new关键字创建的对象在堆内存中分配空间。
对象名stu保存在栈内存中,而对象的属性信息则保存在对应的堆内存中。
创建对象后,可以使用对象访问类的某个属性或方法,对象属性和方法的访问通过点(“.”)运算符实现,具体如下:
对象名.属性名
对象名.方法名
示例:
class Student{
String name;
void read(){
System.out.println("大家好,我是"+name);
}
}
public class Example{
public static void main(String[] args){
Student stu1 = new student();
Student stu2 = new student();
stu1.name = "邵雅琪";
stu1.read();
stu2.name = "李涵";
stu2.read();
}
}
上述示例中,分别定义了Student类和Example类。Student类中声明了name属性和read()方法。Example类的main()方法中创建了两个Student对象————stu1和stu2。
stu1.name = "邵雅琪";
stu1.read();
以上代码为stu1对象的name属性赋值为邵雅琪,通过stu1对象调用read()方法;stu2同理。
stu1对象和stu2对象调用read()方法时,打印的name值不相同。这是因为stu1对象和stu2对象在系统内存中是两个完全独立的个体,它们分别拥有各自的name属性,对stu1对象的name属性进行赋值并不影响stu2对象的name属性的值。
对象的引用传递
类属于引用数据类型,引用数据类型的内存空间可以同时被多个栈内存引用。
示例:
class Student{
String name;
int age;
void read(){
System.out.println("大家好,我是"+name+",年龄"+age);
}
}
class Example{
public static void main(String[] args){
Student stu1 = new student();
Student stu2 = new student();
stu2 = stu1;
stu1.name = "李涵";
stu1.age = 20;
stu2.age = 50;
stu1.read();
stu2.read();
}
}
运行结果:
大家好,我是李涵,年龄50
大家好,我是李涵,年龄50
以上示例分别定义了Student类和Example类。
Student类中声明了name属性、age属性和read()方法。
Example类的main()方法中创建了两个Student对象————stu1和stu2,程序中只对stu1对象进行了实例化,对stu2对象未进行实例化。
stu2 = stu1;
这串代码是stu1对象给stu2对象分配使用权;
stu1.name = "李涵";
stu1.age = 20;
这串代码是分别给stu1对象的name属性和age属性赋值;
stu2.age = 50;
这串代码是对stu2对象的age属性赋值;
stu1.read();
stu2.read();
这串代码分别通过stu1对象和stu2对象调用read()方法。
从输出结果来看,stu1和stu2对象输出的内容是一致的,这是因为stu2对象获得了stu1对象的堆内存空间的使用权。“stu1.age = 20;”这行代码对stu1对象的age属性赋值之后,“stu2.age = 50;”这行代码通过stu2对象对age属性值进行了修改。
实际上,所谓的引用传递,就是将一个堆内存空间的使用权分配给多个栈内存空间使用,每个栈内存空间都可以修改堆内存空间的内容。
一个栈内存空间只能指向一个堆内存空间。如果想要再指向其他堆内存空间,就必须先断开已有的指向,才能分配新的指向。
访问控制权限
java中,针对类,成员变量和属性,java提供了4种访问控制权限,分别是private、default、protected、public。(级别从低到高)
private
私有访问权限 用于修饰类的属性和方法,也可以修饰内部类。类的成员一旦使用了private关键字修饰,则该成员只能在本类中访问
default
默认访问权限 如果一个类的属性或方法没有任何访问权限声明,则该属性或方法就是默认访问权限,可以被本包中的其他类访问,但不能被其他包的类访问。
protected
受保护访问权限 如果一个类中的成员使用了protected关键字修饰,则只能被本包的子类访问。
public
公共访问权限 如果一个类中的成员使用了public关键字修饰,则该成员可以在所有类中被访问,不管是否在同一个包中。
访问范围 | private | default | protected | public |
---|---|---|---|---|
同一个类 | Yes | Yes | Yes | Yes |
同一个包中的类 | no | Yes | Yes | Yes |
不同包的子类 | no | no | Yes | Yes |
全局范围 | no | no | no | Yes |
public class Test{
public int aa;
protected boolean bb;
void cc(){
System.out.println("包访问权限");
}
//private权限的内部类,即私有类,只能在本类中访问
private class InnerClass{
}
}
上面的代码运行会报错❌
外部类的访问权限只能是public和default,所以Test类只能使用public修饰或者不写修饰符。局部成员是没有访问控制权限的。因为局部变量只能在其所在的作用域内起作用,不可能被其他类访问,如果在程序中对局部成员使用访问控制权限修饰符,编译器会报错。
封装性
封装是面向对象的核心思想,掌握封装对于学习java面向对象的内容十分重要。
封装是指将类的实现细节包装、隐藏起来的方法。
封装可以被认为是一道保护屏障,防止本类的代码和数据被外部类定义的代码随机访问。
class Student{
String name;
int age;
void read(){
System.out.println("大家好,我是"+name+",年龄"+age);
}
}
public class Example{
public static void main(String[] args){
Student stu = new Student();
stu.name ="张三";
stu.age = -18;
stu.read();
}
}
在上述示例代码中,age属性赋值为-18岁,这在程序中是不会有任何问题的,因为int的值可以取负值;但在现实中,-18岁明显是一个不合适的年龄值。为了避免这种错误情况发生,在设计Student类时,应该对成员变量的访问做出一些限定,不允许外界随意访问,这就是实现类的封装。
如何实现封装
类的封装是指将对象的状态信息隐藏在对象内部,不允许外部程序直接访问对象的内部信息,而是通过该类提供的方法实现对内部信息的访问。
封装的具体实现过程是:
- 在定义一个类时,将类中的属性私有化,即使用private关键字修饰类的属性。
- 私有属性只能在它所在的类中被访问。
- 如果外界想要访问私有属性,需要提供一些使用public修饰的公有方法,其中包括用于获取属性值的getXxx()方法(也称作getter方法)和设置属性值的setXxx()方法(也称作setter方法)。
class Student{
private String name;
private int age;
public String getName(){
return name;
}
public void setName(String name){
this.name = name;
}
public int getAge(){
return age;
}
public void setAge(int age){
if(age<0){
System.out.println("您输入的年龄有误!");
}else{
this.age = age;
}
}
public void read(){
System.out.println("大家好,我是"+name+",年龄"+age);
}
}
public class Example{
public static void main(String[] args){
Student stu = new Student();
stu.setName("李涵");
stu.setAge("-18");
stu.read();
}
}
以上代码中,使用private关键字将name属性和age属性声明为私有变量,并对外界提供公有的访问方法,其中,getName()方法和getAge()方法用于获取name属性和age属性,setName()方法和getAge()方法用于设置name属性和age属性的值。
运行结果:
您输入的年龄有误
大家好,我是李涵,年龄:0
当调用setAge()方法传入了一个负数(-18)时,系统提示年龄输入有误,age显示为初始值0。
这是因为setAge()方法对参数age进行了判断,如果age的值小于0,会打印”您输入的年龄有误”,age会采用初始值0(在java中int类型的变量初始值为0).
构造方法
实例化一个对象后,如果要为这个对象中的属性赋值,则必须直接访问对象的属性或调用setter方法。
如果需要在实例化对象时为这个对象的属性赋值,可以通过构造方法实现。
构造方法(也称为构造器)是类的一个特殊成员方法,在类实例化对象时自动调用。
定义构造方法
构造方法是一个特殊的成员方法,在定义时,有以下几点需要注意:
- 构造方法的名称必须与类名一致
- 构造方法名称前不能有任何返回值类型的声明
- 不能在构造方法中使用return返回一个值,但可以单独写return语句作为方法的结束。
class Student{
public Student(){
System.out.println("调用了无参构造方法");
}
}
public class Example{
public static void main(String[] args){
System.out.println("声明对象");
Student stu = null;
System.out.println("实例化对象");
stu = new Student();
}
}
运行结果:
声明对象
实例化对象
调用了无参构造方法
当调用关键字new实例化对象时,程序调用了Student类的无参构造方法。
在一个类中除了可以定义无参构造方法外,还可以定义有参构造方法,通过有参构造方法可以实现对属性的赋值。
示例:
class Student{
private String name;
private int age;
public Student(String n,int a){
name = n;
age = a;
System.out.println("调用了有参构造方法");
}
public void read(){
System.out.println("我是:"+name+",年龄"+age);
}
}
public class Example{
public static void main(String[] args){
Student stu = new Student("李涵",18);
stu.read();
}
}
Student类中声明了私有属性name和age, 并且定义了有参构造方法。
“Student stu = new Student(“李涵”,18)”实例化Student对象,并传入参数”李涵”和18,分别赋值给name 和 age, 该过程会调用有参构造方法
运行结果:
调用了有参构造方法
我是:李涵,年龄18
由结果可知:name属性已经被赋值为”李涵“,age属性被赋值为18
class Student{
private String name;
private int age;
public Student(){
System.out.println("调用了一个无参的构造方法")
}
public Student(String n){
name = n;
System.out.println("调用了一个参数的构造方法");
}
public Student(String n,int a){
name = n;
age = a;
System.out.println("调用了两个参数的构造方法");
}
public void read(){
System.out.println("I am"+name+",my age is"+age);
}
}
public class Example{
public static void main(String[] args){
Student stu1 = new Student("李涵");
Student stu2 = new Student("秋",18);
Student stu3 = new Student();
stu1.read();
stu2.read();
stu3.read();
}
}
运行结果:
调用了一个参数的构造方法
调用了两个参数的构造方法
调用了一个无参的构造方法
我是李涵,年龄:0
我是秋,年龄:18
我是null,年龄:0
在上述示例中,Student类中定义了一个无参构造方法和两个有参构造方法。
在main()方法中,根据实例化对象时传入参数个数不同
- stu1对象调用了只有一个参数的构造方法
- stu2对象调用了有两个参数的构造方法
- stu3对象调用了无参的构造方法
默认构造方法:
在java中的每个类都至少有一个构造方法。
如果在一个类中没有定义构造方法,系统会自动为这个类创建一个默认的构造方法,这个默认的构造方法没有参数,方法体中没有任何代码,所以java中默认的构造方法在程序运行时什么也不做。
下面的程序中,Student类的两种写法效果时完全一样的。
第一种写法:
class Student{ }
第二种写法:
class Student{
public Student(){}
}
由于系统提供的默认构造方法往往不能满足需求,因此,通常需要程序员自己在类中定义构造方法,一旦为类定义了构造方法,系统就不再提供默认的构造方法了
示例:
class Student{
int age;
public Student(int n){
age = n;
}
}
上面的Student类中定义了一个有参构造方法,这时系统不再提供默认的构造方法。
public class Example{
public static void main(String[] args){
Student stu = new Student();
}
}
运行上述代码会报错:
java: 无法将类 Student06中的构造器 Student06应用到给定类型;
需要: int
找到: 没有参数
原因: 实际参数列表和形式参数列表长度不同
this关键字
当成员变量与局部变量发生重名问题时,需要使用this关键字分辨成员变量与局部变量。
java中的this关键字语法比较灵活,其作用主要有以下3个:
- 调用本类中的属性
- 调用构造方法
- 调用成员方法
使用this关键字调用本类中的属性
Student类定义中的变量age表年龄,而构造方法中表示年龄的参数是a,这样的程序可读性很差。这时需要对一个类中表示年龄的变量进行统一命名,例如都声明成age。但是这样会导致成员变量和局部变量的命名冲突。
class Student{
private String name;
private int age;
public Student(String name,int age){
name = name;
age = age;
}
public String read(){
return "我是:"+name+",年龄"+age;
}
}
public class Example{
public static void main(String[] args){
Student stu = new Student("李涵",18);
System.out.println(stu.read());
}
}
运行结果:
我是null,年龄:0
根据运行结果可知,stu对象名称为null,年龄为0,这表明构造方法中的赋值并没有成功,这是因为构造方法参数名称与对象成员变量名称相同,编译器无法确定哪个名称是当前对象的属性。
为了解决这个问题,java提供了关键字this指代当前对象,通过this可以访问当前对象的成员。
class Student{
private String name;
private int age;
public Student(String name,int age){
this.name = name;
this.age = age;
}
public String read(){
return "我是:"+name+",年龄"+age;
}
}
public class Example{
public static void main(String[] args){
Student stu = new Student("李涵",18);
System.out.println(stu.read());
}
}
运行结果:
我是李涵,年龄:18
根据运行结果成功调用构造方法完成了stu对象的初始化。
这是因为在构造方法中,使用this关键字明确表示了类中的两个属性this.name和this.age;所以在进行赋值操作时不会产生歧义。
使用this关键字调用成员变量
class Student{
public void openMouth(){
...
}
public void read(){
this.openMouth();
}
}
上述代码中,在read()方法中使用this关键字调用了openMonth()方法。
需要注意的是此处的this关键字也可以省略不写
使用this关键字调用构造方法
构造方法在实例化对象时被java虚拟机自动调用。
在程序中不能像调用其他成员方法一样调用构造方法,但可以在一个构造方法中使用this(参数1,参数2)的形式调用其他的构造方法。
class Student{
private String name;
private int age;
public Student(){
System.out.println("调用了无参构造方法");
}
public Student(String name,int age){
this();
this.name = name;
this.age = age;
}
public String read(){
return "我是:"+name+",年龄"+age;
}
}
public class Example{
public static void main(String[] args){
Student stu = new Student("李涵",18);
System.out.println(stu.read());
}
}
Student类定义了一个无参构造函数和一个有参构造方法,并在有参构造方法中使用this()的形式调用本类中的无参构造方法。
运行结果:
调用了无参构造方法
我是李涵,年龄18
使用this调用类的构造方法时,应注意以下三点:
- 只能在构造方法中使用this调用其他的构造方法,不能在成员变量中通过this调用构造方法。
- 构造方法中,使用this调用其他构造方法的语句必须位于第一行,且只能出现一次。
举例:public Student(String name){ System.out.println("有参构造法方法被调用了"); this(name); //❌❌❌不在第一行,编译错误 }
在 Java 中,构造函数中的 this() 调用必须是构造函数的第一条语句。也就是说,this(name) 必须放在构造函数体的第一行。如果不这样做,编译器会抛出错误,因为 Java 不允许在构造函数中有其他代码(如打印语句)在调用 this() 之前。
正确代码如下✅:public class Student { public Student(String name) { this(name, 18); // 调用另一个构造函数 } public Student(String name, int age) { System.out.println("有参构造法方法被调用了"); // 初始化逻辑 } }
- 不能在一个类的两个构造方法中使用this互相调用。下面写法是错误的❌:
class Student{
public Student(){
this("张三");
System.out.println("有参构造方法被调用");
}
public Student(String name){
this();
System.out.println("无参构造方法被调用")
}
}
错误原因:这种调用方式会导致死循环:无参构造函数调用有参构造函数,而有参构造函数又调用无参构造函数,二者形成了一个循环。由于有参构造函数调用无参构造函数,无参构造函数又调用了有参构造函数,所以这会导致无限递归,从而导致栈溢出错误(StackOverflowError)。
代码块
简单来说,就是用{}括起来的一段代码。
根据位置及声明关键字的不同,代码块可以分为四种:
- 普通代码块
- 构造块
- 静态代码块
- 同步代码块
普通代码块
普通代码块就是直接在方法或语句中定义的代码块
public class Test{
public static void main(String[] args){
{
int age = 18;
System.out.println("这是普通代码块。age:"+age);
}
int age = 30;
System.out.println("age:"+age);
}
}
在上述代码中,每一对{}括起来的代码都是都称为一个代码块。
Test是一个大的代码块,在Test中包含了main()代码块,在main()方法中又定义了一个局部代码块,局部代码块对main()方法1进行了”分割“,起到了限定作用域的作用。
上述代码中,局部代码块定义了变量age,main()方法代码块中也定义了变量age,但由于两个变量处在不同的代码块,作用域不同,因此互不影响。
构造块
构造块是直接在类中定义的代码块。
public class Student09 {
String name;
{
System.out.println("我是构造快");
}
//构造方法
public Student09(){
System.out.println("我是Student类的构造方法");
}
}
public class Example09 {
public static void main(String[] args){
Student09 stu1 = new Student09();
Student09 stu2 = new Student09();
}
}
运行结果:
我是构造快
我是Student类的构造方法
我是构造快
我是Student类的构造方法
其中:
{
System.out.println("我是构造块");
}
这些代码定义的代码块与构造方法、成员属性同级,这样的代码就是构造块。
由此可得两点结论:
- 在实例化Student类对象stu1、stu2时,构造快先于构造方法执行(这里和构造快写在前面还是后面没有关系)
- 每当实例化一个Student类对象时,都会在执行构造方法之前进行构造快。
static 关键字
static关键字,用于修饰类的成员,如成员变量、成员方法以及代码块等,被static修饰的成员具备一些特殊性
静态属性
如果在java程序中使用static修饰属性,则该属性称为静态属性(也称作全局属性)。静态属性可以使用类名直接访问,访问格式:
类名.属性名
class Student{
String name;
int age;
String school = " A 大学";
public Student(String name,int age){
this.name = name;
this.age = age;
}
public void info(){
System.out.println("姓名:"+this.name+",年龄"+this.age+",学校:"+school);
}
}
public class Example{
public static void main(String[] args){
Student stu1 = new Student("李涵",23);
Student stu2 = new Student("雅琪",25);
Student stu3 = new Student("盈盈",20);
stu1.info();
stu2.info();
stu3.info();
//修饰stu1对象的school的值
stu1.school = "B 大学";
System.out.println("修改stu1学校对象的学校信息为B大学后");
stu1.info();
stu2.info();
stu3.info();
}
}
Student类中定义了name属性,age属性和school属性,还定义了有参构造方法和info()方法,并在info()方法中输出了name属性,age属性和school属性的值。
运行结果:
姓名:李涵,年龄23,学校A 大学
姓名:雅琪,年龄25,学校A 大学
姓名:盈盈,年龄20,学校A 大学
修改stu1学校对象的学校信息为B大学后
姓名:李涵,年龄23,学校B 大学
姓名:雅琪,年龄25,学校A 大学
姓名:盈盈,年龄20,学校A 大学
李涵的学校信息由A大学修改为B大学,而雅琪和盈盈的学校信息没有变化,表明非静态属性是对象所有的,改变当前对象的属性值,不影响其他对象的属性值。
下面考虑一种情况:
假设A大学改名为B大学,而此时Student类已经产生了10万个学生对象,那么意味着,如果要修改这些学生对象的学校信息,则要把这10万个学生对象中的school属性全部修改一遍,共修改10万次,这样肯定是非常麻烦的。
那么为了解决这样的问题,可以使用static关键词修饰school属性,将其变为公共属性。这样,school属性就被分配一块内存空间,被school类的所有对象共享,只要某个对象进行了一次修改,全部学生对象的school属性值都会发生变化。
class Student{
String name;
int age;
static String school = "A 大学";
public Student(String name, int age){
this.name = name;
this.age = age;
}
public void info(){
System.out.println("姓名:"+this.name+",年龄:"+this.age+",学校"+school);
}
}
public class Example{
public static void main(String[] args){
Student stu1 = new Student("李涵",23);
Student stu1 = new Student("雅琪",25);
Student stu1 = new Student("盈盈",20);
stu1.info();
stu2.info();
stu3.info();
//修改stu1对象的school值
stu1.school = "B 大学";
System.out.println("修改stu1学校对象的学校信息为B大学后");
stu1.info();
stu2.info();
stu3.info();
}
}
运行结果:
姓名:李涵,年龄23,学校A 大学
姓名:雅琪,年龄25,学校A 大学
姓名:盈盈,年龄20,学校A 大学
修改stu1学校对象的学校信息为B大学后
姓名:李涵,年龄23,学校B 大学
姓名:雅琪,年龄25,学校B 大学
姓名:盈盈,年龄20,学校B 大学
虽然只修改了stu1对象的school属性,但是stu2对象和stu3对象的school属性值也随之变化,说明**使用static关键字声明的属性是所有对象共享的。
static 不能修饰局部变量
static 关键字只能修饰成员变量,不能修饰局部变量,否则编译器会报错:
public class Student{
public void study(){
static int num= 10; //❌❌❌报错,这行代码是非法的
}
}
### 静态方法
如果想要使用类中的成员方法,就需要先将这个类实例化。
而在实际开发时,开发人员有时希望在不创建对象的情况下,通过类名就可以直接调用某个方法,这时就需要使用静态方法,要实现静态方法,只需要在成员方法前加上static关键字。
类名.方法
或
实例对象名.方法
public class Student11 {
private String name;
private int age;
private static String school = "A 大学";
public Student11(String name,int age){
this.name = name;
this.age = age;
}
public void info(){
System.out.println("姓名:"+this.name+",年龄:"+age+",学校:"+school);
}
public static String getSchool(){
return school;
}
public static void setSchool(String s){
school = s;
}
}
public class Example11 {
public static void main(String[] args) {
Student11 stu1 = new Student11("李涵", 15);
Student11 stu2 = new Student11("秦", 20);
Student11 stu3 = new Student11("秋", 25);
System.out.println("---修改前---");
stu1.info();
stu2.info();
stu3.info();
System.out.println("---修改后---");
Student11.setSchool("b大学");
stu1.info();
stu2.info();
stu3.info();
}
}
运行结果:
—修改前—
姓名:李涵,年龄:15,学校:A 大学
姓名:秦,年龄:20,学校:A 大学
姓名:秋,年龄:25,学校:A 大学
—修改后—
姓名:李涵,年龄:15,学校:b大学
姓名:秦,年龄:20,学校:b大学
姓名:秋,年龄:25,学校:b大学
Student类将所有的属性都使用private 关键字进行封装。想要更改属性值,就必须使用setter方法。
由于school属性是用static关键词修饰的,所以可以直接使用类名调用school属性的setter方法。
在main()方法中,”Student11.setSchool(“b大学”);”这行代码直接使用类名Student对静态方法setSchool()进行调用,将静态属性school重新赋值为”B大学”。
static 与非 static 的区别
- static 方法和属性:
- 属于类:static 方法和属性属于类本身,而不是类的任何特定实例。因此,你可以使用类名直接访问它们,例如 Student11.setSchool(“b大学”)。
- 共享性:静态属性在所有实例中共享,即无论你创建多少个 Student11 实例,school 的值都是共享的。
- 非 static 方法和属性:
- 属于实例:非 static 方法和属性属于类的实例。你必须先创建一个对象实例,才能通过该实例访问这些方法或属性。
- 实例独立性:每个实例都有自己的非 static 属性,彼此之间不共享。
静态代码块
用static关键词修饰的代码块就是静态代码块。
当类被加载时,静态代码块就会执行,由于类只加载一次,所以静态代码块只执行一次。
在程序中,通常用于使用静态代码块对类的成员变量进行初始化。
public class Student12 {
String name;
{
System.out.println("我是构造代码块");
}
static {
System.out.println("我是静态代码块");
}
public Student12(){
System.out.println("我是student类的构造方法");
}
}
public class Example12 {
public static void main(String[] args){
Student12 stu1 =new Student12();
Student12 stu2 = new Student12();
Student12 stu3 = new Student12();
}
}
运行结果:
我是静态代码块
我是构造代码块
我是student类的构造方法
我是构造代码块
我是student类的构造方法
我是构造代码块
我是student类的构造方法
由上可知:
代码块的执行顺序为:静态代码块–>构造代码块–>构造方法
static修饰的代码块会随着class文件一同加载,属于优先级最高的代码块
要注意:上例中main()方法中创建了3个Student对象,但在3次实例化对象过程中,静态代码块中的内容只输出了一次,这时因为静态代码块在类第一次使用时才会被加载,并且只会被加载一次。