一、Java 面向对象
1. 面向对象
1.1. 什么是对象?
万物皆对象
所有事物都有属性(描述对象)和行为方法(实现功能)
1.2. 为什么使用面向对象?
对象的具体功能实现细节不对外公开,使用者(调用者)
只需要提供参数给对象的方法,即可返回所要的结果(返回值)我们在需要某种功 能的时候只需要找到了具备该功能的类 即可使用对应功能。
1.2.1. 抽象:
找出事物相似点和共性处,将其归为一类,这个类只考虑共性和相似之处,忽 视无关方面 , 对抽象的理解不要用显微镜去看一个事物的所有方面,这样涉 及的内容就太多了而是要善于划分问题的边界,当前系统需要什么,就只考虑 什么。
1.3. 面向对象三大特征:抽象,封装,继承,多态
1.3.1. 封装性:
封装是将需要保护的内容隐藏起来,保证某些属性和方法不被外部看见 仅对外提供公共访问方式
函数是最小的封装体
1.3.1.1. 封装的实现:
封装性在JAVA中有很多形式,通过权限修饰符private只是最基本 的一种,也是比较常见的形式 ,封装通过修饰权限来实现,当方法或属 性被修饰为私有时只能被定他们的类中使用,不能在该类以外的类直 接访问,达到了隐藏的目的
1.3.1.1.1. 为属性封装: private 属性类型 属性名称
1.3.1.1.2. 为方法封装: private 方法返回类型 方法名称(参数列表){方法}
1.3.1.2. 访问封装的属性的内容
被封装的属性不能被声明属性的类外其他类直接访问,为了能访问属 性的内容,通常通过提供公共的方法getter和setter来分别获取和设置 属性
对私有成员对外提供访问方式 可以在公共方法中加入逻辑判断等语 句,对访问数据进行操作
1.3.1.3. 封装好处:将变化隔离,便于使用,提高重用性,提高安全性
1.3.1.4. 封装原则:将不需要对外提高的内容隐藏起来,把属性隐藏起来提供公共方法来访问
1.3.2. 继承性:
继续就是一个类声
明继承了另外一个类,那么该类就是子类被继承的类就是父类,子父类定义在相同的包时子类可以直接拥有父类的非私有属性和非私有方法,子父类定义在不同的包时子类可以直接拥有父类的public protected修饰的属性与方法.
1.3.2.1. 防止被继承
为了防止一个类被其他类继承,那么可以用final修饰该类
为防止一个类的某些方法被被子类继承可以将其定义为final的
通过private权限修饰符的方法或属性可以被子类继承但不能访问
1.3.2.2. 对继承的方法复写
当子类继承了父类的方法,但想改变具体的实现过程时,可以复写父类 方法,改变功能的具体实现过程
1.3.2.3. 对父类的属性的隐藏
1.3.2.3.1.
如果当子类的属性名跟父类的非私有属性名相同的属性时,该属性隐 藏父类的对应属性,子类对象访问该名称的属性是子类的属性,父类属 性只能通过父类对象访问
此时子类内可以通过super访问到被隐藏的父类的非私有属性
1.3.2.3.2.
子类可以声明跟父类私有属性名相同的属性,两个同名属性分别在子
父类中可访问
1.3.3. 多态性
一个接口引用或父类引用变量可以指向多种实际类型对象,指向谁就可以
调用谁的具体方法,呈现多种形态
多态分为三种方法多态,类多态,接口多态
方法的多态分重载和重写
重载是指同类之中方法的名和返回类型相同,但是参数个数或者参数类型不同
重写是指子类继承父类的方法,但是在子类又写了一个跟父类中的方法名, 返回类型,参数列表都完全一样相同的方法, 但是方法体不同,也就是说它
有自己的实现方式
1.3.3.1. 重写,体现在子父类之间
1.3.3.1.1. 父类的某个方法被其子类重写时,可以各自产生自己的功能行为
当父类的引用指向子类的对象时
a. 该引用可以指向他们的所有子类
b. 该父类的引用只能调用父类中定义的方法和访问父 类的属性
c. 当子类复写了该父类的方法时,父类引用指向哪个子 类就调用哪个子类的复写的方法
1.3.3.2. 重载,体现在同一个类中
1.3.3.3. 实现,体现在接口和实现类之间
1.3.3.3.1. 接口的方法被具体的实现类实现时,可以产生各自的具体功能实现 , 当接口的引用指向了其实现类的对象时
a. 该引用可以指向所有的实现类
b. 所有实现了该接口的类,该接口的引用指向那个实现 类就可以调用哪个类的方法
1.4. 面向过程与面向对象
面向过程以过程为核心,以函数为单元
面向对象以对象为中心,找对象,使用对象,维护对象关系
1.5. 类和对象的关系
类:对现实生活事物的描述,类是对个体中共性内容的属性行为对象的提取,抽象 化
对象:是类中实实在在的个体,对类的实例化,具有类的属性和行为,每个对象都 属于某特定类
对象>>方法/属性>>类/接口>>包>>框架
对象属于某个类
对象具有属性和行为
对象的属性即成员变量
对象的行为即成员方法(函数)
对象的状态即属性取值
变量在行为方法作用下改变取值
一个类的对象特有的属性(变量)和方法(函数)分别属于成员变量和成员方法
一个类的对象共有的属性(变量)和方法(函数)分别属于静态变量和静态方法
非静态只有存在对象才可以访问和调用
静态不依赖于对象存在,可以用类名直接访问和调用
1.6. 对象与对象的关系
整体与部分的关系,这种关系的对象生命周期基本相同
关联关系,两对象没有依赖性,这种关系可以根据需要随时建立随时解除
对象与对象的关系可以是一对一,一对多,多对一,多对多
1.7. 共性抽取
类中相同功能则抽象为接口
类中具有相同属性和行为则抽象为父类
对象中具有相同属性和行为则抽象为类
问题领域相关的事物的属性和行为抽象为对象
将相同的行为实现过程抽象为方法
1.8. 功能的在子父类中的实现
发不同子类之间具有相同功能且实现方法一样时,把该功能放在父类实现,子类
之间继承即可使用
不同子类之间具有相同功能但实现方法不一样,父类仅声明这种功能,子类继承后 分别实现功能
2. 类的定义
class Person()
{
int age; //类的属性,成员变量
String name; //类的属性,成员变量
static String country="CN"; //类的属性,成员变量,静态变量
public void speak() //类的行为,成员函数
{
System.out.println("my name is"+name+"I am "+age);
}
}
产生对象的过程:
Car car = new Car() 类类型变量car跟局部变量一样在栈内存开辟空间,new出一个对象即在堆内存开辟一块空间 成员变量被存入内存中,
car存放着对象实体的内存地址即栈内存中的car变量指向堆内存中的对象,每new一次都会在堆内存产生一个新对象,
当成员变量没有初始化时,产生新对象时,在堆内存中会有默认值
例子
public class Man
{
//对属性的封装,一个人的姓名,年龄,都是这个对象(人)的私有属性
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 && age <130)
this.age = age;
else
System.out,println("error age!");
}
}
3. 构造函数
3.1. 特点:
3.1.1. 函数名与类名相同,不用定义返回类型,可以不写return,对对象进行
初始化的函数
3.1.2. 如果没有定义构造函数,当建立新对象时,也会调用无参数的默认构造函数,而当在类中自定义了构造函数之后,默认的构造函数不再存在, 当定义了含参的构造函数后,若没有定义不含参构造函数,则无法通过无参构造函数来创建对象, 要么定义无参构造函数,要么使用定义的含参构造函数来创建对象
3.2. 构造函数的重载:可以实现不同的初始化方式
3.3. 构造函数与一般函数:
构造函数只在对象建立时自动运行一次,一般方法可以随时被对象调用多次
一个类内一般函数可以被构造函数调用,但一般函数不能调用构造函数
3.4. 构造函数中使用this和super
构造函数内可以通过this关键字来互相调用本类内的构造函数,一个构造函数只能调用一次别的构造函数
子类构造函数可以通过"super.父类构造函数"来调用父类构造函数,一个构造函数只能调用一次父类构造函数 , 调用了父类构造函数就不能调用子类其他的构造函数,
一个构造函数中不同同时使用this和super
3.5. 构造代码块
类中只有函数体没有函数名的代码块,作用是给所有对象进行统一初始化
1)在对象一建立即运行且先于构造函数运行,提取共性初始化内容,而构造函数分 别给对象初始化 , 所以建立对象时,首先运行构造代码块,再运行构造函数,在 创建对象时调用任意构造函数都会调用构造代码块,
2)静态构造代码块在类初始化时调用且只调用一次
3)当构造函数被私有化时,则不能建立对象,故构造函数私有化没有意义
3.6. 构造方法私有化
对于没必要创建对象的类 可以添加一个私有的空参数构造函数,别的类就无法创 建该类的对象了
通常被私有化而不能直接创建对象的类会提供静态方法来获取到该类的实例
4. 关键词this
关键字this :指当前对象自己,方法和构造方法中形参名与类的变量名同名,此时只能直接对参数操作
要操作成员变量需要用this进行引用,哪个对象调用this所在的函数,this就代表哪个对象
1)、this可以作为参数进行传递
如:
public class A {
public A() {
new B(this).print();
}
public void print() {
System.out.println("Hello from A!");
}
}
public class B {
A a;
public B(A a) {
this.a = a;
}
public void print() {
a.print();
System.out.println("Hello from B!");
}
}
运行结果:
Hello from A!
Hello from B!
2),用于区分局部变量与成员变量同名
3),在构造函数中,通过this可以调用同一class中别的构造函数,在一个构造函数中只能调用一个构造函数 ,且只能放在第一行
第一条语句,因为初始化在先,如
public class Flower{
Flower (int petals){}
Flower(String ss){}
Flower(int petals, Sting ss){
//petals++;调用另一个构造函数的语句必须在最起始的位置
this(petals);
//this(ss);会产生错误,因为在一个构造函数中只能调用一个构造函数
}
}
值得注意的是:
a:在构造调用另一个构造函数,调用动作必须置于最起始的位置。
b:不能在构造函数以外的任何函数内调用构造函数。
c:在一个构造函数内只能调用一个构造函数。
5. 变量
5.1. 成员变量与局部变量
作用域:
成员变量作用于整个类中
局部变量作用于函数体或语句块中
位置:
成员变量存在于堆内存中,随对象的产生而产生,若成员变量被static修 饰则随类的加载而产生
局部变量存在于栈内存中
初始化:
成员变量若没有初始化,则产生新对象时,在堆内存中会有默认值
局部变量没有默认初始化值
5.2. 成员变量和静态变量的区别:
1),在内存空间存储的位置。
成员变量存储于对象所在堆内存中。
静态变量存储方法区的静态区中。
2),生命周期。
成员变量随着所属对象的建立而出现,随着所属对象变成垃圾被回收而消失。静态 变量随着所属的类加载而出现,随着类的消失而消失 。
3),调用方式。
成员变量只能被对象调用。
静态变量既可以被对象调用,也可以被类名调用。
4),数据体现。
成员变量的值称为对象中的特有数据。
静态变量的值称为对象中的共享数据。
5.3
成员变量与成员方法的访问权限 修饰符 同类 同包 子类 不同包 public 是 是 是 是 protected 是 是 是 默认 是 是 private 是
5.3. 成员变量什么时候进入内存,进入内存什么位置
位置:静态变量(类变量)在类加载时装入内存方法中,一般成员变量(实例变量) 在创建对象时装入堆内存,
生命周期:静态变量随着类加载而存在,随着类的消失而消失;实例变量随着对象 建立而产生,对象消失而消失
实例变量:每次创建对象都会分配一次内存空间,每个实例变量与其他对象的实体 变量互相独立
java 内存分配分布是怎样的呢, JVM是怎么管理内存的?
一个java程序启动时,一个java虚拟机实例就会产生,类加载器从classpath中找到并 加载class文件,读取class文件中包含的二进制数据并解析
类型信息,获得对象实例, 方法参数,变量属性,返回值,等等信息.虚拟机根据数据生命周期,访问权限,数据类型 等的不同将这些信息分配到
几个不同的内存区域,便于管理.
内存分配
java将运行时数据分配到以下几个区:寄存器,堆(heap) ,栈(stack),方法/代码区 (method)
1) 寄存器
它跟其他其他区的不同点是位于处理器内部,因此存取速度很快,但是容量小, 编译器会根据需要分配
该区的使用权,而我们没有直接控制权
2) 栈
是常规的RAM区,即随机访问存储器,存取速度比堆要快,仅次于直接位于 CPU中的寄存器,每个线程包含一个栈区,每个栈中数据都是私有的其他栈不 能访问,栈分为3部分:基本变量去,执行环境上下文,操作指令区(存放操作指令)
该区通过堆栈指针根据先进后出的改造下移开辟内存空间,指针上移来释放空 间, 该区缺点是只能存储数据长度可知的,存在时间可控的数据,如局部变量, 对象引用变量,数组引用变量,基本数据变量等,缺乏灵活性存放在该区的变量
都要进行显式, 初始化的原因就在于此.当变量所在的函数体或语句块(大括号 内)执行完毕时,该变量的生命周期结束,;立即释放内存
3) 堆
也是常规RAM区,jvm只有一个堆区,所有线程共享,堆中不存放基本类型和对 象引用主要存放实例对象,如String 包装类 实例,可以不必知道数据在该区的 驻留时间,在调用new命令时一个新的对象就会在 该区存在,当创建的对象没 有被任何引用指向时,该对象所占用的空间不会立马被释放掉,而是等待虚拟机 的垃圾回收机制不定时的清除,缺点是要运行时动态分配内存,占用更多的时间, 存取速度慢
4) 方法区/代码区
跟堆一样被被该实例中的所有线程和实例所共享存放整个程序永远唯一的元 素信息如类型信息,类型与超类全限名称,访问修饰,字段信息,方法信息等静态 区属于方法区保存全局变量和静态变量,当类加载时,静态数据也随着加载到该 区,随着类的使用结束而释放空间,静态变量,方法可以直接通过类名访问变量 的6种作用:
全局作用域,文件作用域,命名空间作用域,类作用域,局部作用域,语句作 用域。
堆内存中变量不初始化也有初始值 栈内存中变量不初始化没有初始值
堆与栈区别
局部变量都存于栈内存中
new出来的实体(数组、对象)在堆里 new一次分配一次空间
栈内存空间数据使用完毕自动释放
堆中的实体在没有任何引用引用时将被视为垃圾 不会立即被清除 而是不定 时清除
堆的特点:引用地址、有默认初始值、垃圾回收机制
6. 方法
6.1 方法的定义
方法就是定义在类中的具有特定功能的一段独立小程序。
方法也称为函数。
6.2. 方法的特点:
1)可以将功能代码进行封装
2)便于对功能进行复用
3)方法只有被调用时才会被执行
4) 方法 提高代码复用性
5)若返回类型为void 该方法return语句在最后一行可以不写
6)方法内不能定义方法 只能调用别的方法
注意:
• 方法中只方法调用方法,不可以在方法内部定义方法。
• 定义方法时,方法的结果应该返回给调用者,交由调用者处理。
6.3. 方法的格式
修饰符返回值类型方法名(参数类型形式参数1,参数类型形式参数2,)
{
执行语句;
return 返回值;
}
返回值类型:方法运行后的结果的数据类型。
参数类型:是形式参数的数据类型。
形式参数:是一个变量,用于存储调用函数时传递给方法的实际参数。
实际参数:传递给形式参数的具体数值。
return:用于结束方法。
返回值:该值会返回给调用者。
两个明确
• 明确要定义的功能最后的结果是什么?
• 明确在定义该功能的过程中,是否需要未知内容参与运算
示例:
• 需求:定义一个功能,可以实现两个整数的加法运算。
• 分析:
• 该功能的运算结果是什么?两个数的和,也是一个整数(int)
• 在实现该功能的过程中是否有未知内容参与运算?加数和被加数是不确定的。(两个参数int,int)
代码:
int getSum(int x,int y)
{
return x+y;
}
7. 主函数
主函数是程序的入口,主要用于调用其他的函数,一个类有主函数则该类函数可以独立 运行
主函数用于调用其他函数,所以尽量把独立的函数定义在主函数以外
我们写的程序一般都有的一句话是 public static void main(String[] args)
public 代表该函数访问权限最大
static:代表主函数随类的加载就已经存在
void:主函数没有具体返回值
main 不是关键字 但可以被JVM识别
(String[] args)函数的参数,参数类型是一个字符串数组,表示main函数接收的 是字符串数组。
args 是arguments 的缩写 ,args就是命令行的参数
JVM在调用主函数时,传入的是new String[0]
主函数可以被重载 , 但能被JVM识别并作为入口的固定格式是:public static void main(String [] args){}
.在java解释器解释用户字节码文件时,可以包括需要传给main方法的参数。其一 般形式为:
java类文件名 字符串1 字符串2 。。。。。。字符串n
Example:
public class MainDemo
{ public static void main(String[] args) { for(int i=0;i<args.length;i++) { System.out.println(args[i]); } } }
注意在for循环中,i 的上限值为args.length,他表示传入字符个数,
而下面System.out.println(args[i]);就回对main函数接收的字符串进行输出.
在dos命令行方式下 生成class文件后执行时有:java Hello World ! 后回车, 结果会输出Hello World !
通过main函数的这个程序,我们看到了main函数接收来自系统的字符命令, 故我们不必纠结于main函数返回值,数组类型的参数
8. 对象
8.1. 对象的创建过程
一个程序中可能有很多的类,很多的方法,那么这些类都是什么时候被触发并加载进 内存的呢?
看实例
public class MyDemo {
public static void main(String[] args){
System.out.println("--------以下开始使用Zi类-----");
System.out.println(Zi.s); //访问静态属性
Zi.show();//调用静态方法
System.out.println("---------以下创建对象---------");
Zi p = new Zi("张三");
System.out.println("---------以下调用p对象成员函数---------");
System.out.println(p.getName());
System.out.println("---------以下创建另外一个对象---------");
Zi p1 = new Zi("李四",34);
System.out.println(p.getName()+p1.getName());
System.out.print("对象p1直接调用");
p1.print();
}
}
class Fu{
private String name ;//成员变量
public static String s = "父类静态 变量";//父类静态 变量
static{
System.out.println("父类静态代码块");
}
{
System.out.println("父类构造代码块");
}
public Fu(){
System.out.println("父类不带参数构造函数");
}
public Fu(String name ){
System.out.println("父类带参数构造函数");
this.name = name;
}
public void setName(String name){//成员方法
this.name = name ;
}
public String getName(){
return name;
}
public static void show(){
int i ;
for (i=0;i<2;i++)
System.out.println("父类静态 方法("+i+")");
}
}
class Zi extends Fu{
private int age ;//成员变量
static String s ="子类静态 变量";//子类静态 变量
static{
System.out.println("子类静态代码块");
}
{
System.out.println("子类构造代码块");
}
public Zi(){
System.out.println("子类不带参构造函数");
}
public Zi(String name ) {
super(name);
System.out.println("子类带参数构造函数一");
}
public Zi(String name , int age){
super(name);
this.age =age;
System.out.println("子类带参数构造函数二");
}
public static void print(){
System.out.println("子类静态方法");
}
}
运行结果为:
--------以下开始使用Zi类-----
父类静态代码块
子类静态代码块
子类静态 变量
父类静态 方法(0)
父类静态 方法(1)
---------以下创建对象---------
父类构造代码块
父类带参数构造函数
子类构造代码块
子类带参数构造函数一
---------以下调用成员函数---------
张三
---------以下创建另外一个对象---------
父类构造代码块
父类带参数构造函数
子类构造代码块
子类带参数构造函数二
张三李四
对象p1直接调用子类静态方法
由此可知
该程序启动时虚拟机会首先将程序的入口函数所在的类加载进内存,将类信息存放在方法区中,当类信息加载完毕以后,
虚拟机会依次读取类中main方法的字节码开始执行main下的语句,我们开始访问Zi类的属性时就触发了类加载器加载
Zi和Fu类类信息,首先对Fu类进行初始化包括父类的静态代码块的执行然后是子类初始化,此时就可以直接用类名访问
子父类的静态属性和静态方法了,当我们开始参加对象时,因为所使用的类已经被触发并加载到了内存,所以在创建对象
的时候就不用再初始化静态属性和静态代码块,而只需执行构造函数,在创建对象p的时候依次调用了
>父类构造代码块->父类对应的构造函数 ->子类构造代码块->子类对应的构造函数->
当又创建一个对象时,也同样调用父类构造代码块和父类对应构造函数然后才到子类构造代码块和子类对应构造函数
new出对象的过程总结如下:
-> 父类的静态成员初始化以及静态代码块(依先后顺序)
-> 子类的静态成员初始化以及静态代码块(依先后顺序)
-> 父类的成员属性默认初始化(在子类的构造方法中调用super()时已经将父类和 子类的非静态成员进行默认初始化了)
-> 子类的成员属性默认初始化
-> 父类的成员属性显式初始化
-> 父类的构造代码块
-> 父类的构造方法
-> 子类的成员属性显式初始化
-> 子类的构造代码块
-> 子类的构造方法.
8.2. 匿名对象
匿名对象:不定义对象的引用变量而直接调用构造函数,调用对象的方法
匿名对象访问属性没有意义(除非定义时显示初始化或通过构造方法初始化才可以 访问一次),调用方法才有意义
匿名对象常用于以下情况:
1),对一个对象只需要进行一次方法调用,如果对一个对象进行多个成员的调用, 就必须给这个对象声明引用变量。
2),将匿名对象作为实参传递给一个方法
9. static 关键字
static 关键字:将共享数据(成员变量、成员函数)静态化,那么每个创建的对象都可 以访问到,可以直接用类名调用如:Person.country
9.1. 特点
9.1.1. 它是一个修饰符,用于修饰成员(成员变量和成员函数)
9.1.2. 静态跟类一样只要类存在于内存它就存在于内存,只要类消失那么它也跟随消失,随着类的加载而加载,随着类的消失而消失,其生命周期也最长
9.1.3. 静态的成员跟类一样在内存中只存在一份,被所有该类的对象所共享
9.1.4. 优先于对象存在,只要类被使用到就会随着类的加载而存在于内存
9.1.5. 独立于任何该类的对象,也就是说,它不依赖类特定的实例,被类的所有实例共享。只要这个类被加载,Java虚拟机就能根据类名在运行时数据区的方法区内定找到他们。因此,static对象可以在它的任何对象创建之前访问,无需引用任何对象。
9.1.6. 可以被所有对象调用也可以直接被类名调用
9.1.7. 静态使用注意:静态方法只能访问静态成员(属性和方法)
9.1.8. 静态方法不可以定义this,supper关键字,因为他们代表的是当前调用的引用变量
9.1.9. 对象特有的数据存储到对象的堆内存中, 对象的共享数据存储在方法区的静态区中。
9.2. 静态优缺点
9.2.1. 优点: 对对象共享数据单独存储节省空间,在内存中只有一个拷贝,没必要对每个对象在内存中开辟空间,可以直接被
类名调用(方便)
类名.静态方法名(参数列表...)
类名.静态变量名
9.2.2. 弊端:生命周期过长,访问出现局限性(静态虽好,只能访问静态)
9.3. 静态代码块: 在类加载的时候执行且只执行一次的只有方法体代码块
9.4. 什么时候定义静态方法
当功能内部没有访问到非静态数据时可以将该函数定义为静态
若方法被静态了 , 那么方法里的变量不能再加static,因为方法里面所有变量都被 静态了
成员变量共性提取 static 化方便对象共享
,只有public修饰的类才可以创建帮助文档,类中私有的方法不能 被文档注释
9.5. 通常类是static是不能修饰类的,但static 可以修饰内部类 ,内部类可以看做是一个类的成员
9.6. 静态什么时候用?
1),静态变量。
当对象中的数据都相同时。就将该数据定义成静态的。
被所有对象共享即可。没有必要每一个对象都存储一份,那样只是浪费空间。
2),静态函数。
如果函数中没有访问到对象中的特有数据。
那么该函数需要被静态修饰。不需要创建对象,就可以通过类名访问该函数了。
静态代码块:(随着了类的加载而执行,且只执行一次,用于给类进行初始化,可以通 过静态代码块来验证类是否被加载)
9.7. 静态使用的注意事项:
1),静态方法只能访问静态成员(所以静态虽然多了一种用类名调用 的方式,但是也 出现了访问的局限性)。
非静态方法既可以访问静态又可以访问非静态。
因为非静态们,都所属于对象。
2),静态方法中不可以出现this,super关键字。
因为this代表对象,static方法存在时还没有对象呢.
3),主函数是静态的。
9.8. static final
static final用来修饰成员变量和成员方法,可简单理解为“全局常量”!
对于变量,表示一旦给值就不可修改,并且通过类名可以访问。
对于方法,表示不可覆盖,并且可以通过类名直接访问。