04【面向对象、封装、package、static】
一、面向对象
1.1 面向对象概述
1.1.1 什么是对象
在Java中,对象(Object)是指一个具体事物的实例,任何事物都可以使用对象(类)来描述,如猫、狗、计算机、杯子、云、水、空气、叶子、灰尘等看得见的、看不见的、宏观的、微观的、具体的、抽象的都是对象,总之"万物皆对象";
1.1.2 面向对象程序设计
我们前面就提到过,Java是一门面向对象的编程语言,面向对象是一种程序设计思想,与之对应的还有面向过程程序设计;面向对象是把一个对象的特征(属性)和行为单独封装到对象源代码中;这些属性和行为都被集中到一个地方,这样比把方法或者过程与数据分散开来更为方便和安全,含义更加明确;
1.1.3 面向对象和面向过程
举例:开车去上班
- 面向过程:去车库提车、拿钥匙、打火、踩离合、挂挡、踩油门、刹车、加油、到公司、找车库停车
- 面向对象:找个司机(对象)、到公司
可以看得出来,面向过程关注的是步骤,将所有步骤连在一起"我就能开车到公司上班",面向对象则关注的是"开车去上班"这个事物整体,完成这件事的步骤全部封装起来,交给指定的对象去做(司机);
面向过程适合简单、不需要协作的事务,重点关注如何执行;面向过程来处理事物时,我们首先思考的是"如何按步骤来实现?"一步一步,最终完成,适合简单任务,不需要过多协作的情况;
但是当我们思考较为复杂的设计任务时,比如将"开车"改为了"造车",就会发现我们列出步骤来一步步执行是不现实的,一个汽车厂不可能将造汽车的全部过程都执行一遍,从一个个螺丝的加工生产到发动机的生产、座椅的生产、汽车玻璃的提炼/生产、甚至车载芯片的研发/生产、发动机的研发、电器设备的研发等,相信国内汽车厂没有一家是这样造汽车的,因为自己要做的事物太多了。

这个时候,面向对象更加符合我们的思维方式。我们首先思考的是"车由什么组成?"开始思考问题,为了协作,我们找到轮胎厂采购轮胎(交给轮胎厂对象来生产轮胎),找到玻璃厂采购玻璃(玻璃厂对象来生产玻璃),找到发动机厂采购发动机等,最终进行产品的组装。而不是按步骤一个个造出来。这就是面向对象思维方式;
但是需要注意的是,具体到某个轮胎厂、玻璃厂的一个个流水线操作仍然是有步骤的,仍然离不开面向过程思维;面向对象可以帮助我们从整体上分析整个系统,但是具体到某一个操作上,任然需要面向过程的思路去处理;面向对象和面向过程是相辅相成的,面向对象离不开面向过程;
面向对象与面向过程都是解决问题的一种思维方式,也都是代码的组织方式:
- 面向过程是一种"执行者思维",主要用于解决一些简单问题的场景
- 面向对象是一种"设计者思维",主要解决复杂、需要协作的问题场景
1.1.4 面向对象的特点
面向对象思想是一种更符合我们思考习惯的思想,它可以将复杂的事情简单化,并将我们从执行者变成了指挥者。面向对象的语言中,包含了三大基本特征,即封装、继承和多态。
Tps:关于面向对象和面向过程大家不必太过纠结,我们现在是初学者,理解表面意思即可,随着后面的深入学习,我们会对面向对象有着更深刻的理解;
1.2 类和对象
1.2.1 类和对象的区分
在现实世界中,属于同一类的对象很多,类是抽象的,不是具体的,我们人习惯以对象的方式认识现实世界;
例如我说一辆汽车,那你脑海中立马就能够呈现出一辆汽车的模样吧,不管他是什么品牌、什么颜色、什么价格、什么参数等总而言之,都是一辆汽车,都是属于"汽车类"产品;

tips:类是抽象的,对象是具体的,对象是类的实例化;
类是一组相关属性和行为的集合。可以看成是一类事物的模板,使用事物的属性特征和行为特征来描述该类事物。
- 属性:该事物的状态信息;
- 行为:该事物的功能信息;
1.2.2 类和对象的举例
- 类举例:
举例:猫类
属性:名字、体重、年龄、颜色; 行为:走、跑、叫;
- 实例化对象:
猫对象:
属性:旺财、8kg、6岁、棕色;行为:悄悄的走、飞快的跑、喵喵叫;
1.3 Java类的定义
1.3.1 类的定义格式
public class 类名 {
//成员变量
//成员方法
}
- 定义类:就是定义类的成员,包括成员变量和成员方法。
- 成员变量:和以前定义变量几乎是一样的。只不过位置发生了改变。在类中,方法外。
- 成员方法:和以前定义方法几乎是一样的。只不过把static去掉,static的作用在面向对象后面课程中再详细讲解。
类的定义格式举例:
/**
* @author lscl
* @version 1.0
* @intro:
*/
public class Student {
// 成员变量(属性)
String name; //姓名
int age; //年龄
// 成员方法(行为)
public void study(){
System.out.println("学习");
}
public void eat(){
System.out.println("吃饭");
}
}
1.3.2 类的实例化
前面我们说道过,类是抽象的,不是具体的,类只是负责把事物描述起来,提供模板;对象是类的实例化,是具体的;我们定义好一个Java类后需要通过对象将类进行实例化;
- 创建对象:
类名 对象名 = new 类名();
- 使用对象访问类中的成员:
对象名.成员变量;
对象名.成员方法();
- 练习:
/**
* @author lscl
* @version 1.0
* @intro:
*/
public class Demo01_类的实例化 {
public static void main(String[] args) {
//创建对象格式: 类名 对象名 = new 类名();
Student s = new Student();
System.out.println("s: " + s); //s: com.dfbz.demo.Student@1540e19d
//直接输出成员变量的值
System.out.println("姓名: " + s.name); //null
System.out.println("年龄" + s.age); //0
System.out.println("--------");
//给成员变量赋值
s.name = "刘德华";
s.age = 38;
//再次输出成员变量的值
System.out.println("姓名: " + s.name); //刘德华
System.out.println("年龄: " + s.age); //18
System.out.println("---------");
//调用成员方法
s.study(); //学习
s.eat(); //吃饭
}
}
1.3.3 成员变量的默认值
| 数据类型 | 默认值 | |
|---|---|---|
| 基本类型 | 整数(byte,short,int,long) | 0 |
| 浮点数(float,double) | 0.0 | |
| 字符(char) | 0 | |
| 布尔(boolean) | false | |
| 引用数据类型 | 数组,对象,String | null |
- 定义手机类并使用:
- 属性:品牌,价格,颜色;
- 行为:打电话,发短信
1)定义Phone类:
/**
* @author lscl
* @version 1.0
* @intro:
*/
public class Phone {
//成员变量
String brand;
int price;
String color;
//成员方法
//打电话
public void call(String name){
System.out.println("给"+name+"打电话");
}
//发短信
public void sendMessage(){
System.out.println("大家新年好!");
}
}
2)定义测试类:
/**
* @author lscl
* @version 1.0
* @intro:
*/
public class Demo02_类的实例化小案例 {
public static void main(String[] args) {
// 创建对象
Phone p = new Phone();
System.out.println("品牌: " + p.brand); // null
System.out.println("价格: " + p.price); // 0
System.out.println("颜色: " + p.color); // null
System.out.println("-------");
p.brand = "华为";
p.price = 2999;
p.color = "银白色";
System.out.println("品牌: " + p.brand); //华为
System.out.println("价格: " + p.price); //2999
System.out.println("颜色: " + p.color); //银白色
System.out.println("------------");
p.call("刘德华");
p.sendMessage();
}
}
1.4 对象内存图
回顾JVM内存,JVM总共分为5大内存区域,寄存器、本地方法栈、方法区、栈内存(虚拟机栈)、堆内存;和我们程序员有关的为方法区、栈内存、堆内存;
- 方法区:存储类的信息(有多少变量、方法、是什么修饰符修饰的等)、常量信息、静态变量等信息
- 栈内存(VM栈):方法调用时进栈内存执行,也就是方法运行时消耗的内存;
- 堆内存:存储类的实例信息,只要是new出来的信息都存在堆内存
new这个类的时候,Jvm去方法区找有没有这个class,没有就加载到方法区,属性方法这些都是在方法区class中的;Jvm加载完后,就根据这个模板在堆中创建对象给属性赋默认值,然后再执行赋值语句给对象赋值;
一个对象内存图,示例代码:
/**
* @author lscl
* @version 1.0
* @intro:
*/
public class Demo03_一个对象内存图 {
public static void main(String[] args) {
Phone phone = new Phone();
phone.brand = "小米";
phone.price = 8999;
phone.color = "尊贵金";
phone.call("小红");
phone.sendMessage();
System.out.println("手机的品牌: " + phone.brand);
System.out.println("手机的价格: " + phone.price);
System.out.println("手机的颜色: " + phone.color);
}
}
- 1)首先将主程序所在的类加载到内存(方法区)
- 2)开始执行主程序(main方法)
- 3)执行main方法中的代码
- 4)任何对象在创建之前都需要将其加载到内存(方法区),方法区存储该类的一些基本信息
- 5)加载到方法区后,开始在堆内存中开辟内存空间,对象所有成员变量的值都是存储在堆内存中的
- 6)创建完对象后,开始执行main方法中的其他代码,在调用方法时需要将方法区存储的方法(静态)装载到栈内存中执行,执行方法所消耗的内存都是栈内存,如:方法中的成员变量
- 7)方法执行完毕后,方法中的所有内存都将被释放,这个过程也叫弹栈
- 8)继续调用方法
- 9)继续将方法区存储的方法(静态)装载到栈内存中执行

对象调用方法时,根据对象中方法标记(地址值),去类中寻找方法信息。这样哪怕是多个对象,方法信息只保存一份,节约内存空间。
1.5 局部变量和成员变量
变量根据定义位置的不同,我们给变量起了不同的名字,看下列测试类:
/**
* @author lscl
* @version 1.0
* @intro:
*/
public class Person {
String name; // 成员变量
int age; // 成员变量
/**
* 跑步方法
*/
public void running() {
int count = 10; // 局部变量
System.out.println("跑了" + count + "公里");
}
/**
* 自我介绍方法
*
* @param name: 姓名(局部变量)
* @param age: 年龄(局部变量)
*/
public void intro(String name, int age) {
System.out.println("大家好,我叫" + name + ",今年" + age + "岁");
}
}
- 成员变量和局部变量的区别:
- 成员变量:
- 1)作用域:成员变量定义在类中,在整个类中都可以被访问。
- 2)存储位置:成员变量分为类成员变量和实例成员变量(对象成员变量),实例变量存在于对象所在的堆内存中。
- 3)使用方法:成员变量有默认初始化值。
- 4)权限修饰:成员变量的权限修饰符可以根据需要,选择任意一个
- 局部变量:
- 1)局部变量只定义在局部范围内,如:方法内,代码块内等。
- 2)局部变量存在于栈内存中,当方法弹栈(执行完毕)后,局部变量销毁;
- 3)局部变量没有默认初始化值,使用前必须手动赋值;
- 4)局部变量声明时不指定权限修饰符;
- 成员变量:
1.6 值传递和引用传递
1.6.1 思考
/**
* @author lscl
* @version 1.0
* @intro:
*/
public class Demo04_值传递与引用传递 {
public static void main(String[] args) {
Phone p1=new Phone();
p1.price=2899;
test(p1);
System.out.println(p1.price); // 2899 or 3899?
int i=20;
test2(i);
System.out.println(i); // 20 or 50?
}
public static void test(Phone p){
p.price=3899;
}
public static void test2(int i){
i=50;
}
}
1.6.2 形参和实参
形参也叫形式参数,是一个方法的参数列表中的参数;实参也叫实际参数,是调用者在调用方法时实际传递的参数;

1.6.3 值传递和引用传递概念
- 值传递(参数类型是基本数据类型):方法调用时,实参把它的值传递给对应的形参,形参只是用实参的值初始化自己的存储单元内容,是两个不同的存储单元,所以方法执行中形参值的改变不影响实参的值。
- 引用传递:(参数类型是引用数据类型参数):也称为传地址。方法调用时,实参是对象(或数组),这时实参与形参指向同一个地址,在方法执行中,对形参的操作实际上就是对实参的操作,这个结果在方法结束后被保留了下来,所以方法执行中形参的改变将会影响实参。
Tips:在Java中,除了基本数据类型之外的数据类型都是引用数据类型,都是通过new在堆内存开辟空间;
值传递与引用传递:
/**
* @author lscl
* @version 1.0
* @intro:
*/
public class Demo04_值传递与引用传递 {
public static void main(String[] args) {
Phone p1=new Phone();
p1.price=2899;
test(p1);
System.out.println(p1.price); // 2899 or 3899?
int i=20;
test2(i);
System.out.println(i); // 20 or 50?
}
public static void test(Phone p){
p.price=3899;
}
public static void test2(int i){
i=50;
}
}

1.6.4 小练习
- 示例代码:
/**
* @author lscl
* @version 1.0
* @intro:
*/
public class Demo05_值传递和引用传递小练习{
public static void main(String[] args) {
String[] arr1 = {"1", "2", "3"};
String[] arr2 = {"100", "200", "300"};
test(arr1, arr2);
System.out.println("-------arr1-------");
for (int i = 0; i < arr1.length; i++) {
System.out.print(arr1[i] + ','); // 1,2,3
}
System.out.println();
System.out.println("-------arr2-------");
for (int i = 0; i < arr2.length; i++) { // 100,200,300
System.out.print(arr2[i] + ',');
}
}
// 0x11 0x22
public static void test(String[] arr1, String[] arr2) {
String[] temp;
// 0x11
temp = arr1;
// 0x22
arr1 = arr2;
// 0x11
arr2 = temp;
}
}
1.7 匿名对象
顾名思义,匿名就是没有名字的对象,在创建对象时,只通过new的动作在堆内存开辟空间,却没有把堆内存空间的地址值赋值给栈内存的某个变量用以存储;
使用场景:
- 1)如果对一个对象只需要进行一次方法调用,那么就可以使用匿名对象。
- 2)我们经常将匿名对象作为实参传递给一个方法调用。
/**
* @author lscl
* @version 1.0
* @intro:
*/
public class Demo06_匿名对象 {
public static void main(String[] args) {
Student student=new Student();
intro(student);
student.study();
// 使用匿名对象传递
intro(new Student());
// 使用匿名对象调用方法
new Student().study();
}
public static void intro(Student student) {
System.out.println("姓名: "+student.name+",年龄: "+student.age);
}
}
二、封装
2.1 封装概述
封装是面向对象的三大特征之一,面向对象编程语言是对客观世界的模拟,客观世界里成员变量都是隐藏在对象内部的,外界无法直接操作和修改。封装可以被认为是一个保护屏障,防止该类的代码和数据被其他类随意访问。要访问该类的数据,必须通过指定的方式。适当的封装可以让代码更容易理解与维护,也加强了代码的安全性
- 举例:
就拿一辆汽车来说,外部有一个壳,将内部的原件封装起来,至于汽车内部的细节是什么,我们不需要关心,对于用户来说,我们只需要会操作这辆汽车就可以,汽车对外提供方向盘、离合器、油门、以及一些娱乐设备,这样就将内部的东西不在直接暴露给外部,增加了安全性;
我们来举例一段代码,看看没有封装的对象会存在什么问题:
准备一个Person类,具备姓名、年龄等属性
/**
* @author lscl
* @version 1.0
* @intro:
*/
class Person {
// 学生姓名
String name;
// 学生年龄
int age;
}
编写测试类:
/**
* @author lscl
* @version 1.0
* @intro:
*/
public class Demo01_没有封装的情况 {
public static void main(String[] args) {
Person p1 = new Person();
p1.name = "小灰";
p1.age = 2800;
intro(p1);
Person p2 = new Person();
p2.name = "小蓝";
p2.age = -280;
intro(p2);
}
public static void intro(Person person) {
System.out.println("大家好,我叫" + person.name + ",今年" + person.age + "岁");
}
}

上述代码是可以正常运行的(编译不会报错),但是对于我们程序逻辑来说是错误的;
Student类未进行封装,其所有属性直接暴露给外部,外部程序可以任意的对Student的属性就行修改,这是非常有安全隐患的,我们的正常年龄可以设置一个正常区间如0~120岁,不可能到2800多岁,也不可能是负的岁数;因此我们要对属性加以管控,而不是直接暴露给外端;
2.2 private 关键字
- private 是一个权限修饰符,代表最小权限。
- 可以修饰成员变量和成员方法。
- 被private修饰后的成员变量和成员方法,只在本类中才能访问。
封装的原则:将属性隐藏起来,若需要访问某个属性,提供公共方法对其访问
1)使用private 修饰成员变量,代码如下:
public class Student {
// 学生姓名
private String name;
// 学生年龄
private int age;
}
2)提供 getXxx 方法 / setXxx 方法,可以访问成员变量,代码如下
/**
* @author lscl
* @version 1.0
* @intro:
*/
public class Student {
// 私有成员变量,外界不能(直接)访问这个成员了
private String name;
private int age;
// 外界可以通过这个方法来设置name属性
public void setName(String stuName) {
name = stuName;
}
// 外界可以通过这个方法来设置age属性
public void setAge(int stuAge) {
if (stuAge > 0 && stuAge < 120) {
age = stuAge;
System.out.println("赋值成功,您的年龄为: " + stuAge);
} else {
System.out.println("您输入的年龄不合法," + stuAge);
}
}
// 外界可以通过这个方法来访问name
public String getName() {
return name;
}
// 外界可以通过这个方法来访问age
public int getAge() {
return age;
}
public void show() {
System.out.println("大家好,我叫【" + this.name + "】,今年【" + this.age + "】岁");
}
}
3)测试类:
/**
* @author lscl
* @version 1.0
* @intro:
*/
public class Demo01 {
public static void main(String[] args) {
Student s1 = new Student();
s1.setName("小灰");
s1.setAge(2888);
s1.setAge(-280);
s1.setAge(20);
}
public static void intro(Student student) {
System.out.println("大家好,我叫" + student.getName() + ",今年" + student.getAge() + "岁");
}
}

经过封装后,属性再也不是直接暴露给外部了,我们可以在外部操作属性之前加以控制;
2.3 this 关键字
2.3.1 this的使用
this是Java中的一个关键字,代表所在类的当前对象的引用(地址值),即对象自己的引用;
- 定义一个Cat类:
/**
* @author lscl
* @version 1.0
* @intro:
*/
public class Cat {
public void show() {
System.out.println("我的内存地址值是: " + this);
}
}
- 测试类:
/**
* @author lscl
* @version 1.0
* @intro:
*/
public class Demo03_this关键字的介绍 {
public static void main(String[] args) {
Cat c1 = new Cat();
System.out.println(c1); // Cat@1540e19d
// Cat@1540e19d
c1.show();
System.out.println("----------");
Cat c2 = new Cat();
System.out.println(c2); // Cat@677327b6
// Cat@677327b6
c2.show();
}
}
2.3.2 this的内存图分析
- 定义一个Worker类:
/**
* @author lscl
* @version 1.0
* @intro:
*/
public class Worker {
// 私有化
private String name;
private int age;
// 对外提供的修改方法
public void setName(String name) { // 就近原则
// this代表当前对象的内存地址值
this.name = name;
}
public void setAge(int age) {
// this代表当前对象的内存地址值
this.age = age;
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
public void show() {
System.out.println("大家好,我叫【" + name + "】,今年【" + age + "】岁");
}
}
- 测试代码:
/**
* @author lscl
* @version 1.0
* @intro:
*/
public class Demo04_this关键字的内存图 {
public static void main(String[] args) {
Worker w1=new Worker();
w1.setName("小红");
w1.setAge(20);
w1.show();
System.out.println("------------------");
Worker w2=new Worker();
w2.setName("小龙");
w2.setAge(30);
w2.show();
}
}
this内存图:

Tips :方法被哪个对象调用,方法中的this就代表那个对象。即谁在调用,this就代表谁。
2.4 构造方法
构造方法也叫构造器,顾名思义就是用来构造类的;当一个对象被创建时候,构造方法用来初始化该对象,给对象的成员变量赋初始值。
Tips:无论你与否自定义构造方法,所有的类都有构造方法,因为Java自动提供了一个无参数构造方法,一旦自己定义了构造方法,Java自动提供的默认无参数构造方法就会失效。
- 构造方法的定义格式
修饰符 构造方法名(参数列表){
// 方法体
}
构造方法的写法上,方法名与它所在的类名相同。它没有返回值,所以不需要返回值类型,甚至不需要void。使用构造方法后,代码如下:
/**
* @author lscl
* @version 1.0
* @intro:
*/
public class Phone {
private String name;
private int price;
// 无参构造方法
public Phone() {
}
// 有参构造方法
public Phone(String name, int price) {
this.name = name;
this.price = price;
}
}
构造方法注意事项:
- 如果你不提供构造方法,系统会给出无参数构造方法。
- 如果你提供了构造方法,系统将不再提供无参数构造方法。
- 构造方法是可以重载的,既可以定义参数,也可以不定义参数。
2.5 标准JavaBean
JavaBean 是 Java语言编写类的一种标准规范。符合 JavaBean 的类,要求类必须是和公共的,并且具有无参数的构造方法,提供用来操作成员变量的 set 和 get 方法,采用private修饰成员变量。
- 格式如下:
public class ClassName{
//成员变量
//构造方法
//无参构造方法【必须】
//有参构造方法【建议】
//成员方法
//getXxx()
//setXxx()
}
编写符合 JavaBean 规范的类,以学生类为例,标准代码如下:
/**
* @author lscl
* @version 1.0
* @intro:
*/
public class Phone {
//成员变量
private String brand;
private int price;
//构造方法
public Phone() {
}
public Phone(String brand, int price) {
this.brand = brand;
this.price = price;
}
//成员方法
public String getBrand() {
return brand;
}
public void setBrand(String brand) {
this.brand = brand;
}
public int getPrice() {
return price;
}
public void setPrice(int price) {
this.price = price;
}
}
三、package包
3.1 包的概述
Java允许使用包(package)将类组织再一个集合中。包类似于我们计算机中的文件夹,方便我们对Java类型进行管理。借助包可以方便地组织自己的代码;
package语句作为Java源文件的第一条语句,指明该文件中定义的类所在的包。(若缺省该语句,则指定为无名包)。它的格式为:
package 顶层包名.子包名;
查看我们的代码目录:

所有的类都在一个目录下(根目录),随着项目的类越来越,我们的项目会变得难以维护;包(package)类似于我们的文件夹,当文件多了,我们可以创建多个文件夹对文件进行归类,包则是对Java源代码进行归类;
3.2 创建包
在src目录上右击--->New--->Package:

包的名称一般为公司倒写域名,例如com.alibaba、com.baidu等;
.代表分割,com.dfbz实际创建了一个二级目录;

也可以通过创建类的方式来创建包:
输入com.dfbz.Demo02:在com/dfbz包下创建了一个Demo02类:

输入com.abc.Demo01:在com/abc包下创建了一个Demo01类:

最终目录结构:

3.3 声明包
在任何类的第一句代码都必须声明这个类在哪个包下的,我们之前的类都创建在根目录(src目录),因此不需要声明所在的包,除了根目录下的类都需要在类的第一行声明包;

1)包对应于文件系统的目录,package语句中,用 “.” 来指明包(目录)的层次;
2)包通常用小写单词,类名首字母通常大写;
3.4 import导入包
为使用定义在不同包中的Java类,需用import语句来引入指定包层次下所需要的类或全部类(.*)。import语句告诉编译器到哪里去寻找类

如果想要导入这个包下的所有类,使用*:
import com.dfbz.*;
3.5 不同包的同名类
不同的包下是可以存在同名类的,就跟计算机的不同文件夹下可以存在同名的文件一样,当一个类需要用到这个同名类时我们需要精确导入;
在不同包下准备两个同名类:
Tips:访问不同包下类的成员时,必须保证该属性被public修饰,否则不能访问(关于权限修饰符我们后面再详细了解)
- Car:
package com.dfbz.demo01;
/**
* @author lscl
* @version 1.0
* @intro:
*/
public class Car {
public String name;
public String brand;
}
- Car:
package com.dfbz.demo02;
/**
* @author lscl
* @version 1.0
* @intro:
*/
public class Car {
public String name;
public String color;
}
- 测试类:
package com.dfbz.demo03;
import com.dfbz.demo01.Car;
/**
* @author lscl
* @version 1.0
* @intro:
*/
public class Demo01 {
public static void main(String[] args) {
Car car1 = new Car();
car1.name = "比亚迪-汉";
car1.brand = "比亚迪";
// 已经导入过了一个Car类,接下来需要精确导入
com.dfbz.demo02.Car car2 = new com.dfbz.demo02.Car();
car2.name = "红旗-HS7";
car2.color = "黑色";
}
}
包结构如下:

四、static关键字
4.1 static概述
static 关键字它可以用来修饰的成员变量和成员方法,被修饰的成员是属于类的,而不是单单是属于某个对象的。被static修饰的成员由该类的所有实例(对象)共享;
4.2 定义和使用格式
4.2.1 类变量
当 static 修饰成员变量时,该变量称为类变量。该类的每个对象都共享同一个类变量的值。任何对象都可以更改该类变量的值,但也可以在不创建该类的对象的情况下对类变量进行操作,因为该变量属于类,而不是某个对象。
- 类变量:使用 static关键字修饰的成员变量。
定义格式:
static 数据类型 变量名;
举例:
static String className;
类变量是属于"类的变量",不属于某个对象,类变量被所有对象共享,类变量可以通过类名访问,也可以通过对象名来访问。
示例,定义一个Person类,创建一些类变量:
package com.dfbz.demo01;
/**
* @author lscl
* @version 1.0
* @intro:
*/
public class Person {
public static String className = "Person";
public static String classInfo= "我是一个Person类型,包名是com.dfbz.demo01";
}
运行测试类:
package com.dfbz.demo01;
/**
* @author lscl
* @version 1.0
* @intro:
*/
public class Demo01_访问静态成员变量 {
public static void main(String[] args) {
Person p1=new Person();
Person p2=new Person();
// 通过对象名可以访问类变量
System.out.println(p1.className); // Person
System.out.println(p2.className); // Person
// 通过类名也可以访问类变量(推荐方式)
System.out.println(Person.className); // Person
}
}
4.2.2 静态方法
当 static 修饰成员方法时,该方法称为类方法。静态方法在声明中有 static ,建议使用类名来调用,而不需要创建类的对象。调用方式非常简单。
- 类方法:使用 static关键字修饰的成员方法,习惯称为静态方法。
定义格式:
修饰符 static 返回值类型 方法名 (参数列表) {
// 执行语句
}
举例:在Person类中定义静态方法
public static void showInfo(){
System.out.println("我是一个Person类");
System.out.println("我所在的包是com.dfbz.demo01");
}
测试代码:
package com.dfbz.demo01;
/**
* @author lscl
* @version 1.0
* @intro:
*/
public class Demo02_访问静态成员方法 {
public static void main(String[] args) {
Person p1 = new Person();
Person p2 = new Person();
// 可以通过对象名来调用方法
p1.showInfo();
p2.showInfo();
// 也可以通过类名来调用方法
Person.showInfo();
}
}
4.2.3 静态方法的特点
静态方法调用的注意事项:
- 静态方法可以直接访问类变量(被static修饰的变量)和静态方法。(静态方法能够访问静态资源)
- 静态方法不能直接访问普通成员变量或成员方法。反之,成员方法可以直接访问类变量或静态方法。
- 静态方法中,不能使用this关键字。
Tips:静态方法只能访问静态成员。
示例代码:
package com.dfbz.demo01;
/**
* @author lscl
* @version 1.0
* @intro:
*/
public class Demo03_静态成员方法的注意事项 {
public static void main(String[] args) {
Demo03Example.method();
}
}
class Demo03Example {
public static int a = 10;
public int b = 20;
public static void method() {
System.out.println(a);
// System.out.println(b); // 访问出错,静态方法中不能访问非静态的成员
// System.out.println(this.b); // 访问出错,静态方法中不能访问this
}
}
4.3 静态原理图解
static 修饰的内容:
- 1)是随着类的加载而加载的,且只加载一次。
- 2)存储于一块固定的内存区域(方法区),可以直接被类名调用。
- 3)它优先于对象存在,所以,可以被所有对象共享。
定义一个Student类:
package com.dfbz.demo01;
/**
* @author lscl
* @version 1.0
* @intro:
*/
public class Student {
public String name;
public int age;
public Student(String name, int age) {
this.name = name;
this.age = age;
}
public static String className = "Student";
public void say() {
System.out.println("大家好");
}
public static void showClass() {
System.out.println("我是一个Student类,我拥有3个变量和两个方法");
}
}
测试类:
package com.dfbz.demo01;
/**
* @author lscl
* @version 1.0
* @intro:
*/
public class Demo04_静态成员的内存图 {
public static void main(String[] args) {
Student s1=new Student("小灰",20);
Student s2=new Student("小蓝",30);
}
}
内存图:

4.4 代码块
4.4.1 构造代码块
- 代码块:每当对象创建时,对象的代码块将会被执行(优先于构造方法执行)
格式:
public class ClassName {
{
//执行语句
}
}
作用:给对象进行初始化赋值。用法演示,代码如下:
public class Person {
private int age;
private String name;
{
// 默认每个对象的age都为18
this.age = 18;
// 执行语句
}
}
4.4.2 静态代码块
- 静态代码块:定义在成员位置,使用static修饰的代码块{ }。
- 位置:类中方法外。
- 执行:随着类的加载执行,而执行且执行一次。
格式:
public class ClassName {
static {
//执行语句
}
}
作用:给类变量进行初始化赋值。用法演示,代码如下:
package com.dfbz.demo01;
public class Computer {
private String name;
private double price;
// 类一加载,静态代码块中的代码就会被执行,而且是执行一次
static{
System.out.println("加载网卡驱动....");
System.out.println("加载声卡驱动....");
System.out.println("加载第三方软件插件....");
}
}
Tips:static 关键字,可以修饰变量、方法和代码块。在使用的过程中,其主要目的还是想在不创建对象的情况下,去调用方法。下面将介绍两个工具类,来体现static 方法的便利。
静态代码块在类加载的时候执行;
4.4.3 代码块小练习
- 示例代码:
package com.dfbz.demo01;
/**
* @author lscl
* @version 1.0
* @intro:
*/
public class Demo05_静态代码块 {
public static void main(String[] args) {
new A(); // 1,3,2
new A(); // 3,2
}
}
class A {
static {
System.out.println("1");
}
public A() {
System.out.println("2");
}
{
System.out.println(3);
}
}

浙公网安备 33010602011771号