1、static
1.1、基本概念:
-
使用static关键字修饰成员变量表示静态的含义,此时成员变量由对象层级提升为类层级,也就是整个类只有一份并被所有对象共享,该成员变量随着类的加载准备就绪,与是否创建对象无关。
-
static关键字修饰的成员可以使用引用.的方式访问,但推荐类名.的方式。
/*
编程实现People类的封装
*/
public class People {
// 1.私有化成员变量,使用private关键字修饰
private String name;
private int age;
private String country; // 隶属于对象层级,也就是每个对象都拥有独立的一份
}
public class PeopleTest {
public static void main(String[] args) {
// 1.使用有参方式构造两个People类型的对象并打印特征
People p1 = new People("zhangfei", 30/*, "China"*/);
p1.show(); // zhangfei 30 China
People p2 = new People("guanyu", 35/*, "China"*/);
p2.show(); // guanyu 35 China
}
每个对象都有自己country,这个相同的东西,可以用同一个内存,减少内存的浪费,需要用static修饰,整个类只有一个,被大家共享。(修改People类)
// 1.私有化成员变量,使用private关键字修饰
private String name;
private int age;
//private String country; // 隶属于对象层级,也就是每个对象都拥有独立的一份
//public static String country; // 隶属于类层级,也就是整个类只有一份并且被所有对象共享
private static String country;
}
完整代码:
public class People {
// 1.私有化成员变量,使用private关键字修饰
private String name;
private int age;
//private String country; // 隶属于对象层级,也就是每个对象都拥有独立的一份
//public static String country; // 隶属于类层级,也就是整个类只有一份并且被所有对象共享
private static String country;
// 3.在构造方法中调用set方法进行合理值的判断
public People() {}
public People(String name, int age/*, String country*/) {
setName(name);
setAge(age);
//setCountry(country);
}
// 2.提供公有的get和set方法,并在方法体中进行合理值的判断
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 < 150) {
this.age = age;
} else {
System.out.println("年龄不合理哦!!!");
}
}
public static String getCountry() {
return country;
}
public static void setCountry(String country) {
//this.country = country;
People.country = country;
}
public void show() {
System.out.println("我是" + getName() + ",今年" + getAge() + "岁了,来自" + getCountry());
}
}
/*
编程实现People类的测试
*/
public class PeopleTest {
public static void main(String[] args) {
// 3.验证static关键字修饰的静态成员(类成员)是否与创建对象无关 类名.的方式 => 无关
//System.out.println("获取到的国籍信息是:" + People.country); // null
System.out.println("获取到的国籍信息是:" + People.getCountry()); // null
// 1.使用有参方式构造两个People类型的对象并打印特征
People p1 = new People("zhangfei", 30/*, "China"*/);
p1.show(); // zhangfei 30 China
People p2 = new People("guanyu", 35/*, "China"*/);
p2.show(); // guanyu 35 China
System.out.println("--------------------------------------------");
// 2.验证static关键字修饰的静态成员(类成员) 是否被所有对象共享 => 共享
//p1.country = "蜀国";
p1.setCountry("蜀国");
//System.out.println("第一个对象的国籍是:" + p1.country); // 蜀国
//System.out.println("第二个对象的国籍是:" + p2.country); // 蜀国
System.out.println("第一个对象的国籍是:" + p1.getCountry()); // 蜀国
System.out.println("第二个对象的国籍是:" + p2.getCountry()); // 蜀国
People p3 = new People();
//System.out.println("第三个对象的国籍是:" + p3.country); // 蜀国
System.out.println("第三个对象的国籍是:" + p3.getCountry()); // 蜀国
}
}
1.2、static的使用:
- 在非静态成员方法中既能访问非静态的成员又能访问静态的成员。
(成员:成员变量 + 成员方法, 静态成员被所有对象共享)
- 在静态成员方法中只能访问静态成员不能访问非静态成员。
(成员:成员变量 + 成员方法, 因为此时可能还没有创建对象)
- 在以后的开发中只有隶属于类层级并被所有对象共享的内容才可以使用
(关键字修饰。不能滥用static关键字)
public class StaticTest {
private int cnt = 1; // 隶属于对象层级,也就是每个对象都拥有独立的一份
private static int snt = 2; // 隶属于类层级,也就是所有对象都共享同一份
// 自定义非静态的成员方法 需要使用引用.的方式访问
public void show() {
System.out.println("cnt = " + this.cnt); // 1
System.out.println("snt = " + this.snt); // 2 静态成员被所有对象共享,this关键字可以省略
}
// 自定义静态的成员方法 推荐使用类名.的方式访问
public static void test() {
// StaticTest st = new StaticTest();
//System.out.println("cnt = " + cnt); // 1 静态成员方法中没有this关键字,因为是可以通过类名.方式调用的
System.out.println("snt = " + snt); // 2
}
public static void main(String[] args) {
StaticTest st = new StaticTest();
st.show();
System.out.println("--------------------------------");
StaticTest.test();
}
}
1.3、构造块和静态代码块
-
构造块:在类体中直接使用 { } 括起来的代码块。
-
每创建一个对象都会执行一次构造块。
-
静态代码块:使用static关键字修饰的构造块。
-
静态代码块随着类加载时执行一次。 (静态代码块会随着类的加载而准备就绪,会先于构造块执行)
-
静态代码块先于构造块执行,构造块先于构造体之前执行
public class BlockTest {
// 当需要在执行构造方法体之前做一些准备工作时,则将准备工作的相关代码写在构造块中即可,比如:对成员变量进行的统一初始化操作
{
System.out.println("构造块!"); // (2)
}
// 静态代码块会随着类的加载而准备就绪,会先于构造块执行
// 当需要在执行代码块之前随着类的加载做一些准备工作时,则编写代码到静态代码块中,比如:加载数据库的驱动包等
static {
System.out.println("#####################静态代码块!"); // (1)
}
// 自定义构造方法
public BlockTest() {
System.out.println("====构造方法体!"); // (3)
}
public static void main(String[] args) {
BlockTest bt = new BlockTest();
BlockTest bt2 = new BlockTest();
}
}
1.4、main方法详解
public class MainTest {
public static void main(String[] args) {
System.out.println("参数数组中元素的个数是:" + args.length);
System.out.println("传递给main方法的实际参数为:");
for(int i = 0; i < args.length; i++) {
System.out.println("下标为" + i + "的形参变量数值为:" + args[i]);
}
}
}
重点案例:单例设计模式
1、单例设计模式的概念:
- 在某些特殊场合中,一个类对外提供且只提供一个对象时,这样的类叫做单例类,而设计单例的流程和思想叫做单例设计模式。
2、单例设计模式的实现流程:
· 私有化构造方法,使用private关键字修饰。
· 声明本类类型的引用指向本类类型的对象,并使用private static关键字共同修饰。
· 提供公有的get方法负责将对象返回出去,并使用private static关键字共同修饰
3、单例设计模式的实现方式:
· 单例设计模式的实现方式有两种:饿汉式 和 懒汉式,在以后的开发中推荐饿汉式。
编程实现Singleton类的封装。
编程实现SingletonTest类对Singleton类进行测试,要求main方法中能得到且只能得到该类的一个对象。
public class Singleton {
// 2.声明本类类型的引用指向本类类型的对象,使用private static关键字共同修饰
//private static Singleton sin = new Singleton(); // 饿汉式
private static Singleton sin = null; // 懒汉式
// 1.私有化构造方法,使用private关键字修饰
private Singleton() {}
// 3.提供公有的get方法负责将对象返回出去,使用public static关键字共同修饰
public static Singleton getInstance() {
//return sin;
if(null == sin) {
sin = new Singleton();
}
return sin;
}
}
public class SingletonTest {
public static void main(String[] args) {
// 1.声明Singleton类型的引用指向该类型的对象
//Singleton s1 = new Singleton();
//Singleton s2 = new Singleton();
//System.out.println(s1 == s2); // 比较变量s1的数值是否与变量s2的数值相等 false
//Singleton.sin = null; 可以使得引用变量无效
Singleton s1 = Singleton.getInstance();
Singleton s2 = Singleton.getInstance();
System.out.println(s1 == s2); // true
}
}
- sin用static修饰后就隶属于类层级,也就是整个类只有一份并且被所有对象共享;
- 加private后私有化,防止通过类名.引用(Singleton.sin = null;)
- 私有化后不能调用,通过公有的get方法返回值(唯一出口)
- 整个sin加载在方法里
2、继承
继承的概念:
当多个类之间有相同的特征和行为时,可以将相同的内容提取出来组成 一个公共类,让多个类吸收公共类中已有特征和行为而在多个类型只需
要编写自己独有特征和行为的机制,叫做继承。
在java语言中使用extends(扩展)关键字来表示继承关系。
如:public class Worker extends Person{} -表示Worker类继承自Person类
–其中Person类叫做超类、父类、基类。
–其中Worker类叫做派生类、子类、孩子类。
· 使用继承提高了代码的复用性,可维护性及扩展性,是多态的前提条件。
public class Worker extends Person {
}
2.1、继承的特点
-
子类不能继承父类的构造方法和私有方法,但私有成员变量可以被继承只是不能直接访问。
-
无论使用何种方式构造子类的对象时都会自动调用父类的无参构造方法,来初始化从父类中继承的成员变量,相当于在构造方法的第一行增加代码super()的效果。
-
· 使用继承必须满足逻辑关系:子类is a 父类,也就是不能滥用继承。
-
语言中只支持单继承不支持多继承,也就是说一个子类只能有一个父类,但一个父类可以有多个子类。
Person
public class Person {
// 1.私有化成员变量,使用private关键字修饰
private String name;
private int age;
//private boolean gender; // 性别
// 3.在构造方法中调用set方法进行合理值的判断
public Person() {
System.out.println("Person()");
}
public Person(String name, int age) {
System.out.println("Person(String, int)");
setName(name);
setAge(age);
}
// 2.提供公有的get和set方法并在方法体中进行合理值的判断
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 < 150) {
this.age = age;
} else {
System.out.println("年龄不合理哦!!!");
}
}
// 自定义成员方法实现特征的打印
public void show() {
System.out.println("我是" + getName() + ",今年" + getAge() + "岁了!");
}
// 自定义成员方法描述吃饭的行为
public void eat(String food) {
System.out.println(food + "真好吃!");
}
// 自定义成员方法描述娱乐的行为
public void play(String game) {
System.out.println(game + "真好玩!");
}
}
Work类继承Person
public class Worker extends Person {
private int salary;
public Worker() {
super(); // 表示调用父类的无参构造方法,若没有加则编译器自动添加
System.out.println("Worker()");
}
public Worker(String name, int age, int salary) {
super(name, age); // 表示调用父类的有参构造方法
System.out.println("Worker(String, int, int)");
//setName(name);
//setAge(age);
setSalary(salary);
}
public int getSalary() {
return salary;
}
public void setSalary(int salary) {
if(salary >= 2200) {
this.salary = salary;
} else {
System.out.println("薪水不合理哦!!!");
}
}
// 自定义成员方法描述工作的行为
public void work() {
System.out.println("今天的砖头有点烫手...");
}
// 自定义show方法覆盖从父类中继承的版本
@Override // 标注/注解,用于说明下面的方法是对父类方法的重写,若没有构成重写则编译报错
public void show() {
super.show(); // 表示调用父类的show方法
System.out.println("我的薪水是:" + getSalary());
}
}
WorkTest测试
public class WorkerTest {
public static void main(String[] args) {
// 1.使用无参方式构造Worker类型的对象并打印特征
Worker w1 = new Worker();
// 当子类重写show方法后,则下面调用的是重写以后的版本
w1.show(); // null 0
System.out.println("----------------------------------");
// 2.使用有参方式构造Worker类型的对象并打印特征
Worker w2 = new Worker("zhangfei", 30, 3000);
w2.show(); // zhangfei ...
// 调用成员方法测试
w2.eat("豆芽");
w2.play("王者荣耀");
w2.work();
}
}
2.2、方法的重写
从父类中继承下来的方法不满足子类的需求时,就需要在子类中重新写一个和父类一样的方法来覆盖从父类中继承下来的版本,该方式就叫做方法的重写(Override)。
方法重写的原则
- 要求方法名相同、参数列表相同以及返回值类型相同,从Java5开始允许返回子类类型。
//要求返回类型一致(Person)
//父类
public Person show(){}
//子类
public Person show(){}
//java5后面就可以不一样,可以返回子类类型
//父类
public Person show(){}
//子类
public Work show(){}
-
要求方法的访问权限不能变小,可以相同或者变大。子类对父类的扩展,大等于(public>private)
-
要求方法不能抛出更大的异常异常机制。
方法重写的步骤:
1、@Override注释
2、调用父类的方法,用super.show()
public class Person {
public void show() {
System.out.println("我是" + getName() + ",今年" + getAge() + "岁了!");
}
}
public class Worker extends Person {
// 自定义show方法覆盖从父类中继承的版本
@Override // 标注/注解,用于说明下面的方法是对父类方法的重写,若没有构成重写则编译报错
public void show() {
super.show(); // 表示调用父类的show方法
System.out.println("我的薪水是:" + getSalary());
}
}
2.3、构造块和静态代码块的考点
-
先执行父类的静态代码块,再执行子类的静态代码块。
-
执行父类的构造块,执行父类的构造方法体。
-
执行子类的构造块,执行子类的构造方法体。
package com.lagou.task08;
import java.sql.SQLOutput;
public class SuperTest {
{
System.out.println("SuperTest类中的构造块!"); // (2) c
}
static {
System.out.println("SuperTest类中的静态代码块!"); // (1) a
}
public SuperTest() {
System.out.println("SuperTest类中的构造方法体!"); // (3) d
}
public static void main(String[] args) {
// 使用无参方式构造对象
SuperTest st = new SuperTest();
}
}
package com.lagou.task08;
// 导入java目录中lang目录中System类中的静态成员out 很少使用
import static java.lang.System.out;
public class SubSuperTest extends SuperTest {
{
System.out.println("==========SubSuperTest类中的构造块!"); // (2) e
}
static {
System.out.println("==========SubSuperTest类中的静态代码块!"); // (1) b
}
public SubSuperTest() {
//System.out.println("==========SubSuperTest类中的构造方法体!"); // (3) f
out.println("==========SubSuperTest类中的构造方法体!");
}
public static void main(String[] args) {
// 使用无参方式构造子类的对象
SubSuperTest sst = new SubSuperTest();
}
}
2.4、常用的访问控制符
-
public修饰的成员可以在任意位置使用。
-
private修饰的成员只能在本类内部使用。
-
通常情况下,成员方法都使用public关键字修饰,成员变量都使用private关键字修饰。
2.5、final
基本概念
final本意为“最终的、不可改变的”,可以修饰类、成员方法以及成员变量。
使用方式
-
final关键字修饰类体现在该类不能被继承。
-主要用于防止滥用继承,如:java.lang.String类等。 -
final关键字修饰成员方法体现在该方法不能被重写但可以被继承。
-主要用于防止不经意间造成重写,如:java.textDateformat类中的format方法等。
-
final关键字修饰成员变量体现在该变量必须初始化且不能改变。
-主要用于防止不经意间造成改变,如:java.lang.Thread类中MAX_PRIORITY等。
public /*final*/ class FinalClass {
public final void show() {
System.out.println("FinalClass类中的show方法!");
}
}
public class SubFinalClass extends FinalClass {
/*@Override
public void show() {
super.show();
}*/
public static void main(String[] args) {
SubFinalClass sfc = new SubFinalClass();
sfc.show();
}
}
public class FinalMemberTest {
// private final int cnt = 1; // 显式初始化
private final int cnt;
/*{
cnt = 2; // 构造块中进行初始化
}*/
public FinalMemberTest() {
cnt = 3; // 构造方法体中进行初始化
}
public static void main(String[] args) {
// 声明FinalMemberTest类型的引用指向该类的对象
FinalMemberTest fmt = new FinalMemberTest();
// 打印成员变量的数值
System.out.println("fmt.cnt = " + fmt.cnt); // 0 1 2 3
}
}