活动地址:CSDN21天学习挑战赛
在类中声明的实例变量,它的值是每个对象独立的。但是有些成员变量的值不能每个对象独一份,需要成为共享数据
。
在类中声明的实例方法,想要调用它时就必须先创建对象,使用对象来调用,但是有些方法是和当前类对象无关的。
对于以上的两种情况,我们就需要将类的成员变量和成员方法声明成静态的(static)
。
7.1.2 静态变量
有static修饰的成员变量就是静态变量
。
[修饰符] class 类 {
[修饰符] static 数据类型 静态变量名;
}
- 静态变量的默认值就是其所对应的
数据类型的默认值
- 静态变量是
所有对象共享的
- 静态变量的值
存储在方法区
- 静态变量在本类中,可以在任何方法、代码块、构造器中
直接使用
- 如果
权限修饰符允许,在其他类中可以通过“类名.静态变量”直接访问
,也可以通过“对象.静态变量”的方式访问(但是更推荐使用类名.静态变量的方式)。静态变量的get/set方法也是静态的
,当局部变量与静态变量重名时,使用”类名.静态变量“进行区分。
静态内容是优先于对象存在
,只能访问静态,不能使用this/super。静态修饰的内容存于静态区。静态成员只能访问静态成员
,这是生命周期原因,反过来是可以用的 main方法为一个程序入口,可以应用于任何类。静态代码块,只执行一次
示例:
public class Student {
private String name ;
private int age ;
static int num ;
public Student() {
}
public Student(String name, int age) {
this.name = name;
this.age = age;
}
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;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
", num=" + num +
'}';
}
}
class Test1 {
public static void main(String[] args) {
// 通过类名.静态变量名访问静态变量
System.out.println("num的默认值:" + Student.num);
Student s1 = new Student("张三" , 23);
// 在修饰符允许的情况下,可以通过对象.静态变量名进行访问静态变量,但是不建议
s1.num = 3 ;
System.out.println(s1);
Student s2 = new Student("李四" , 25);
System.out.println(s2);
}
}
静态类变量(静态变量):和类有关,存储在方法区,有默认值,是所有对象所共享的,生命周期和类相同,可以使用权限修饰符修饰
非静态实例变量(实例变量):跟对象有关,存储在堆中,有默认值,每一个对象的实例变量是独立的,生命周期和对象相关,可以使用权限修饰符修饰
局部变量:和方法有关,存储在栈中,没有默认值,每一次调用方法都是独立的,作用域就是方法体内,只能用final修饰符修饰
有static修饰的成员方法就是静态方法
[修饰符] class 类 {
[修饰符] static 返回值类型 方法名(形参列表) {
方法体
}
}
静态方法在本类的任意方法、代码块、构造器中都可以被直接调用
只要权限修饰符允许,静态方法在其他类中可以通过“类名.静态方法“的方式调用。也可以通过”对象.静态方法“的方式调用(但是更推荐使用类名.静态方法的方式)。
静态方法可以被子类继承,但不能被子类重写。
public class Animal {
private String name ;
private int age ;
{
System.out.println("这是一个非静态代码块");
method();
}
public Animal() {
System.out.println("无参构造");
method();
}
public Animal(String name, int age) {
System.out.println("有参构造");
this.name = name;
this.age = age;
method();
}
public static void method() {
System.out.println("静态方法method()被调用");
}
public void getInfo() {
System.out.println("name:" + name + ",age:" + age);
method();
}
}
class Cat extends Animal{
public Cat() {
}
public Cat(String name, int age) {
super(name, age);
}
// public void method() {
// System.out.println("静态方法method()被调用");
// }
}
class Test4 {
public static void main(String[] args) {
Cat cat = new Cat();
Animal.method();
Cat.method();
}
}
如果想要为静态变量初始化,可以直接在静态变量的声明后面直接赋值,也可以使用静态代码块。
代码块的前面加static,就是静态代码块
[修饰符] class 类名 {
static {
静态代码块
}
}
每一个类中的静态代码块只会执行一次,且静态代码块的执行优先于构造器和非静态代码块
示例:
public class StaticCode {
static {
System.out.println("StaticCode类中的静态代码块");
}
{
System.out.println("StaticCode类中的非静态代码块");
}
public StaticCode() {
System.out.println("无参构造器");
}
}
class Test1 {
public static void main(String[] args) {
StaticCode s1 = new StaticCode();
System.out.println("------------------");
StaticCode s2 = new StaticCode();
}
}
静态代码块和非静态代码块都是写在类中方法外的
静态代码块先于非静态代码块执行
静态代码块在类初始化时执行,且只执行一次
非静态代码块在实例化对象时执行,每创建一个对象都会执行一次非静态代码块
类的初始化就是为静态语句初始化的。实际上,类初始化的过程时在调用一个clinit()方法,而这个方法是编译器自动生成的。编译器会将静态类成员变量的显示赋值语句和静态代码块中的语句按顺序合并到类初始化clinit()方法中
每一个类都只会进行一次初始化,如果子类初始化时发现父类还未初始化,那么会先将父类初始化
类的初始化一定是优先于对象的实例化的
public class Father {
static {
System.out.println("Father的静态代码块1 , a = " + Father.a);
}
private static int a = 8 ;
static {
System.out.println("Father的静态代码块1 , a = " + Father.a);
}
public static void method() {
System.out.println("Father.method");
}
}
class Test2 {
public static void main(String[] args) {
Father.method();
Father f1 = new Father();
Father f2 = new Father();
}
}
public class Animal {
static {
System.out.println("Animal类中的静态代码块");
}
}
class Cat extends Animal {
static {
System.out.println("Cat类中的静态代码块");
}
}
class Test3 {
public static void main(String[] args) {
Cat cat = new Cat();
}
}
public class Person {
static {
System.out.println("Person类的静态代码块");
}
{
System.out.println("Person类的非静态代码块");
}
public Person() {
System.out.println("Person类的无参构造");
}
}
class Test4 {
public static void main(String[] args) {
Person person = new Person();
}
}
即:
静态直接访问静态,可以
非静态直接访问非静态,可以
非静态直接访问静态,可以
静态访问非静态,不可以
静态的类变量和静态的方法可以通过“类名.
”的方式直接访问;也可以通过“对象."的方式访问。(但是更推荐使用”类名."的方式)
非静态的实例变量和非静态的方法只能通过“对象."方式访问。
静态的方法和静态的代码块中,不允许出现this和super关键字,如果有重名问题,使用“类名.”进行区别。(this和super关键字都是和对象有关的)
非静态的方法和非静态的代码块中,可以使用this和super关键字。
某些类型的对象是有限的几个
,例如:
星期:Monday(星期一)…Sunday(星期天)
性别:Man(男)、Woman(女)
月份:January(1月)…December(12月)
季节:Spring(春节)…Winter(冬天)
- ……
枚举类型本质上也是一种类,只不过是这个类的对象是固定的几个,而不能随意让用户创建。
在JDK1.5之前,需要程序员自己通过特殊的方式来定义枚举类型。
在JDK1.5之后,Java支持enum关键字来快速的定义枚举类型。
在JDK1.5之前声明枚举类
构造器加private
私有化
本类内部创建一组常量对象,并添加public static
修饰符,对外暴露这些常量对象
示例代码:
/**
* JDK1.5之前定义枚举类的方法
*/
public class Season {
public static final Season SPRING = new Season() ;
public static final Season SUMMER = new Season() ;
public static final Season AUTUMN = new Season() ;
public static final Season WINTER = new Season() ;
private Season() {}
@Override
public String toString() {
if (this == SPRING) {
return "春" ;
}else if (this == SUMMER) {
return "夏" ;
} else if (this == AUTUMN) {
return "秋" ;
} else {
return "冬" ;
}
}
}
class TestSeason {
public static void main(String[] args) {
Season spring = Season.SPRING ;
System.out.println(spring); // 春
System.out.println(Season.SPRING); // 春
}
}
格式:
[修饰符] enum 枚举类名 {
常量对象列表
}
[修饰符] enum 枚举类名 {
常量对象列表;
其他成员列表;
}
代码示例:
/**
* 枚举类,JDK1.5以后新增的枚举类声明方式
*/
public enum Week {
MONDAY , TUESDAY , WEDNESDAY , THURSDAY , FRIDAY , SATURDAY , SUNDAY
}
class TestWeek {
public static void main(String[] args) {
Week week = Week.FRIDAY ;
System.out.println(week); // FRIDAY
}
}
枚举类的要求和特点:
枚举类的常量对象列表必须在枚举类的首行
,因为是常量,所以建议大写。如果常量对象列表后面没有其他代码,那么“
;
”可以省略,否则不可以省略“;”。编译器给枚举类
默认提供的是private的无参构造
,如果枚举类需要的是无参构造,就不需要声明,写常量对象列表时也不用加参数,如果枚举类需要的是有参构造,需要手动定义,有参构造的private可以省略,调用有参构造的方法就是在常量对象名后面加(实参列表)就可以。
枚举类默认继承的是
java.lang.Enum
类,因此不能再继承其他的类型。
JDK1.5之后switch,提供支持枚举类型,case后面可以写枚举常量名。
枚举类型如有其它属性,建议(不是必须)这些属性也声明为
final
的,因为常量对象在逻辑意义上应该不可变。
示例代码:
public enum Week1 {
MONDAY("星期一") ,
TUESDAY("星期二") ,
WEDNESDAY("星期三") ,
THURSDAY("星期四") ,
FRIDAY("星期五") ,
SATURDAY("星期六") ,
SUNDAY("星期日") ;
private final String description ;
private Week1(String description) {
this.description = description ;
}
@Override
public String toString() {
return super.toString()+ ":" + description;
}
}
class TestWeek1 {
public static void main(String[] args) {
System.out.println(Week1.SATURDAY);
Week1 week1 = Week1.TUESDAY;
switch (week1) {
case MONDAY:
System.out.println("今天是周一");
break;
case TUESDAY:
System.out.println("今天是周二");
break;
case WEDNESDAY:
System.out.println("今天是周三");
break;
case THURSDAY:
System.out.println("今天是周四");
break;
case FRIDAY:
System.out.println("今天是周五");
break;
case SATURDAY:
System.out.println("今天是周六");
break;
case SUNDAY:
System.out.println("今天是周日");
break;
}
}
}
枚举类型常用方法
String toString()
: 默认返回的是常量名(对象名),可以继续手动重写该方法
String name()
:返回的是常量名(对象名)
int ordinal()
:返回常量的次序号,默认从0开始
枚举类型[] values()
:返回该枚举类的所有的常量对象,返回类型是当前枚举的数组类型,是一个静态方法
枚举类型 valueOf(String name)
:根据枚举常量对象名称获取枚举对象
示例:
public class Demo2 {
public static void main(String[] args) {
System.out.println(Season1.SPRING.name()); // 返回的是常量名(对象名)
System.out.println(Season1.WINTER.ordinal()); // 返回常量的次序号,默认从0开始
Season1[] season1s = Season1.values(); // 返回该枚举类的所有的常量对象,返回类型是当前枚举的数组类型,是一个静态方法
for (Season1 season1 : season1s) {
System.out.println(season1);
}
Season1 spring = Season1.valueOf("SPRING");
System.out.println(spring);
}
}
enum Season1 {
/**
* 季节枚举类
*/
SPRING("春天") , SUMMER("夏天") , AUTUMN("秋天") ,WINTER("冬天") ;
private final String description ;
Season1(String description) {
this.description = description;
}
@Override
public String toString() {
return super.toString() + ":" + description;
}
}
不具体、或无法具体
例如:当我们声明一个几何图形类:圆、矩形、三角形类等,发现这些类都有共同特征:求面积、求周长、获取图形详细信息。那么这些共同特征应该抽取到一个公共父类中。但是这些方法在父类中又无法给出具体的实现,而是应该交给子类各自具体实现。那么父类在声明这些方法时,就只有方法签名,没有方法体,我们把没有方法体的方法称为抽象方法。Java语法规定,包含抽象方法的类必须是抽象类。
使用abstract关键字修饰的类就是抽象类
这种类不能被创建对象,它一般就是用来做父类的,被子类继承的
抽象方法:
被abstract修饰
没有方法体的方法
抽象方法的作用:
- 强制要求子类必须重写
抽象类
- 被abstract修饰的类。
抽象类的语法格式:
[权限修饰符] abstract class 类名 {
}
[权限修饰符] abstract class 类名 extends 类名 {
}
抽象方法的语法格式
[修饰符] abstract 返回值类型 方法名([形参列表]) ;
示例:
/**
* 定义一个抽象类
*/
public abstract class Animal {
public abstract void eat() ; // 动物都会有的行为,定义为抽象方法,必须要实现
public abstract void sleep() ;
// 一些动物会有的行为定义为普通方法,不要求必须重写
public void fly() {
}
}
class Cat extends Animal{
@Override
public void eat() { // 小猫类实现吃东西的方法
System.out.println("小猫吃鱼");
}
@Override
public void sleep() { // 小猫类实现睡觉的方法
System.out.println("小猫睡觉");
}
}
class TestAbstract {
public static void main(String[] args) {
Animal cat = new Cat() ;
cat.eat(); // 小猫吃鱼
cat.sleep(); // 小猫睡觉
}
}
此时的放法重写是子类对父类抽象方法的完成实现,我们将这种叫做方法的重写,也叫方法的实现
有抽象方法的类一定是抽象类
抽象类的子类,必须重写抽象父类中所有的抽象方法,否则,编译无法通过而报错。除非该子类也是抽象类。
普通类+抽象方法