Java 第二阶段提升编程能力 【面向对象编程(高级部分)】
代码链接:https://download.csdn.net/download/qq_52354698/86263857?spm=1001.2014.3001.5503
1. 类变量和类方法
1. 什么是类变量
类变量也叫静态变量/静态属性,是该类的所有对象共享的变量,任何一个该类的对象去访问它时,取到的都是相同的值,同样任何一个该类的对象去修改它时,修改的也是同一个变量。
2. 如何定义类变量
访问修饰符 static 数据类型 变量名;
static 访问修饰符 数据类型 变量名;
3. 如何访问类变量
类变量的访问必须遵循其访问权限
类名.类变量名//类变量是随类的加载而创建的,即使没有创建对象实例也可以访问
对象名.类变量名
4. 类变量使用细节和注意事项
1. 什么时候需要用类变量
当我们需要让某个类的所有对象都共享一个变量时,就可以考虑使用类变量(静态变量):比如:定义学生类,统计所有学生共交多少钱。Student(name,fee)
2. 类变量与实例变量(普通属性)的区别
类变量是该类的所有对象共享的,而实例变量是每个对象独享的。
3. 加上static称为类变量或静态变量,否则称为实例变量/普通变量/非静态变量
4. 类变量可以通过 类名.类变量名 或者 对象名.类变量名 类访问,但java设计者推荐我们使用 类名.类变量名 方式访问。【满足修饰符的访问权限和范围】
5. 实例变量不能通过 类名.类变量名 方式访问
6. 类变量是在类加载时就初始化了,也就说,即使没有创建对象,只要类加载了,就可以使用类变量了
7. 类变量的生命周期是随类的加载而开始,随着类的消亡而销毁
5. 类方法基本介绍
类方法也叫静态方法。
访问修饰符 static 数据返回类型 方法名() {}
static 访问修饰符 数据返回类型 方法名() {}
6. 类方法的调用
满足访问修饰符的访问权限和范围
类名.类方法名
对象名.类方法名
7. 类方法的使用场景
当方法中不涉及到任何和对象相关的成员,则可以将方法设计成静态方法,提供开发效率。
实际开发中,往往将一些通用的方法,设计成静态方法
8. 类方法使用细节和注意事项
- 类方法和普通方法都是随着类的加载而加载,将结构信息存储在方法区。
- 类方法中没有this参数
- 普通方法中国隐含着this的参数
- 类方法可以通过类名调用,也可以通过对象名调用
- 普通方法和对象有关,需要通过对象名调用,比如对象名.方法名(参数),不能通过类名调用。
- 类方法中不允许使用和对象有关的关键字,比如this和super。普通方法(成员方法)可以。
- 类方法(静态方法)中只能访问静态变量或静态方法。
- 普通成员方法,既可以访问普通变量(方法),也可以访问静态变量(方法)。
静态方法,只能访问静态的成员
非静态方法,可以访问静态成员和非静态成员
2. 理解main方法语法说明
public static void main(String[] args) {
}
- main方法是虚拟机调用
- java虚拟机需要调用类的main()方法,所以该方法的访问权限必须是public
- java虚拟机在执行main()方法时不必创建对象,所以该方法必须是static
- 该方法接收String类型的数组参数,该数组中保存执行java命令时传递给所运行的类的参数
- java执行的程序 参数1 参数2 参数3 …
特别提示
- 在mian()方法中,我们可以直接使用main()方法所在类的静态方法或静态属性。
- 但是,不能直接访问该类中的非静态成员,必须创建该类的一个实例对象后,才能通过这个对象去访问类中的非静态成员。
3. 代码块
1. 基本介绍
代码化块又称为初始化块,属于类中成员 [ 即 是类的一部分 ],类似于方法,将逻辑语句封装在方法体中,通过{}包围起来。
但和方法不同,没有方法名,没有返回,没有参数,只有方法体,而不用通过对象或类显式调用,而是加载类时,或创建对象时隐式调用。
2. 基本语法
[修饰符] {
代码
};
注意:
- 修饰符可选,要写的话,只能写 static
- 代码块分为两类,使用 static 修饰的叫静态代码块,没有 static 修饰的,叫普通代码块
- 逻辑语句可以为任何逻辑语句(输入、输出、方法调用、循环、判断等)
- ; 好可以写上,也可以省略
package com.qdu.codeblock;
public class CodeBlock01 {
public static void main(String[] args) {
Movie movie = new Movie("大闹天宫");
System.out.println("===============");
Movie movie1 = new Movie("西天取经", "2002.12.12");
}
}
class Movie {
private String name;
private String date;
{
System.out.println("广告1开始...");
System.out.println("广告2开始...");
System.out.println("广告3开始...");
}
public Movie(String name) {
System.out.println("public Movie(String name) 被调用");
this.name = name;
}
public Movie(String name, String date) {
System.out.println("public Movie(String name, String date) 被调用");
this.name = name;
this.date = date;
}
}
代码块的理解
- 相等于另外一种形式的构造器(对构造器的补充机制),可以做初始化的操作
- 如果多个构造器中都有重复的语句,可以抽取到初始化块中,提供代码的重用性
3. 代码块使用细节和注意事项
- static 代码块也叫静态代码块,作用就是对垒进行初始化,而且它随着类的加载而执行,并且只会执行一次。如果是普通代码块,每创建一个对象,就执行一次。
- 类什么时候被加载
a. 创建对象实例时(new)
b. 创建子类对象实例,父类也会被加载
c. 使用类的静态成员时(静态属性,静态方法)
package com.qdu.codeblock;
public class CodeBlockDetail01 {
public static void main(String[] args) {
AA aa = new AA();
System.out.println("=================");
BB bb = new BB();
System.out.println("=================");
Cat cat = new Cat();
}
}
class Cat {
public static int n1 = 999;
static {
System.out.println("Cat 的静态代码块被执行...");
}
}
class BB {
static {
System.out.println("BB 的静态代码块被执行...");
}
}
class AA extends BB{
static {
System.out.println("AA 的静态代码块被执行...");
}
}
- 普通的代码块,在创建对象实例时,会被隐式的调用。
被创建一次,就会调用一次。
如果只是使用类的静态成员时,普通代码块冰不会执行。
1. static 代码块是类加载时,执行,只会执行一次
2. 普通代码块是在创建对象时调用的,创建一次,调用一次
3. 类加载的3种情况,需要记住
创建一个对象时,在一个类,调用顺序是:
a. 调用静态代码块和静态属性初始化(注意:静态代码块和静态属性初始化调用的优先级一样,如果有多个静态代码块和多个静态变量初始化,则按他们定义的顺序调用)
b. 调用普通代码块和普通属性的初始化(注意:普通代码块和普通属性初始化调用的优先级一样,如果有多个普通代码块和多个普通属性初始化,则按定义顺序调用)构造方法(构造器)的最前面其实隐含了 super() 和调用普通代码块
我们看一下创建一个子类时(继承关系),他们的静态代码块,静态属性初始化,普通代码块,普通属性初始化,构造方法的调用顺序如下:
a. 父类的静态代码块和静态属性(优先级一样,按定义顺序执行)
b. 子类的静态代码块和静态属性(优先级一样,按定义顺序执行)
c. 父类的普通代码块和普通属性初始化(优先级一样,按定义顺序执行)
d. 父类的构造方法
e. 子类的普通代码块和普通属性初始化(优先级一样,按定义顺序执行)
f. 子类的构造方法静态代码块只能直接调用静态成员(静态属性和静态方法),普通代码块可以调用任意成员
4. 单例设计模式
1. 什么是设计模式?
- 静态方法和属性的经典使用
- 设计模式是在大量的实践中总结和理论化之后优选的代码结构、编程风格、以及解决问题的思考方式。
2. 什么是单例模式
- 所谓类的单例设计模式,就是采用一定的方法保证在整个软件系统中,对某个类只能存在一个对象实例,并且该类只提供一个取得其对象实例的方法。
- 单例模式有两种方式:a. 饿汉式,b. 懒汉式
3. 单例模式应用实例
- 构造器私有化:防止用户直接new对象
- 类的内部创建对象(static)
- 向外暴露一个静态的公共方法(static)
- 代码实现
饿汉式
package com.qdu.single_;
public class SingleTon01 {
public static void main(String[] args) {
GirlFriend girlFriend = GirlFriend.getInstance();
System.out.println(girlFriend);
}
}
class GirlFriend {
private String name;
private static GirlFriend girlFriend = new GirlFriend("小红");
private GirlFriend(String name) {
this.name = name;
}
public static GirlFriend getInstance() {
return girlFriend;
}
@Override
public String toString() {
return "GirlFriend{" +
"name='" + name + '\'' +
'}';
}
}
懒汉式
package com.qdu.single_;
public class SingleTon02 {
public static void main(String[] args) {
Cat cat = Cat.getInstance();
System.out.println(cat);
}
}
class Cat {
private static String name;
private static Cat cat;
private Cat(String name) {
this.name = name;
}
public static Cat getInstance() {
if (cat == null) {
cat = new Cat("小可爱");
}
return cat;
}
@Override
public String toString() {
return "Cat{" +
"name='" + name + '\'' +
'}';
}
}
4. 饿汉式VS懒汉式
- 二者最主要的区别在于创建对象的时机不同:饿汉式是在类加载就创建了对象实例,而懒汉式是在使用时才创建。
- 饿汉式不存在线程安全问题,懒汉式存在线程安全问题。
- 饿汉式存在浪费资源的可能。因为如果程序员一个对象实例都没有使用,那么饿汉式创建的对象就浪费了,懒汉式是使用时才创建,就不存在这个问题。
5. final关键字
1. 基本介绍
final中文的意思:最后的,最终的。
final可以修饰、属性、方法和局部变量。
在某些情况下,程序员可能会有一下需求,就会使用到final:
- 当不希望类被继承时,可以使用final修饰。
- 当不希望父类的某个方法被子类覆盖/重写时,可以使用final关键字修饰。
- 当不希望类的某个属性的值被修改,可以用final修饰。
- 当不希望某个局部变量被修改,可以使用final修饰。
2. final注意事项和使用细节
- final修饰的属性又叫常量,一般用XX_XX_XX来命名。
- final修饰的属性在定义时,必须赋初值,并且以后不能再修改,赋值可以在以下位置之一:
a. 定义时:如 public final double TAX_RATE = 0.08;
b. 在构造器中
c. 在代码块中 - 如果final修饰的属性是静态的,则初始化的位置只能是定义时、静态代码块中,不能在构造器中赋值。
- final类不能继承,但是可以实例化对象。
- 如果类不是final类,但是含有final方法,则该方法虽然不能重写,但是可以被继承。
- 一般来说,如果一个类已经是final类了,就没有必要再将方法修饰成final方法了。
- final不能修饰构造方法(即构造器)
- final和static往往搭配使用,效率更高,不会导致类加载,底层编译做了优化处理。
- 包装类(Integer、Double、Float、Boolean等都是final),String也是final类。
3. final实例
请编写一个程序,能够计算圆形的面积。要求圆周率为3.14。
package com.qdu.final_;
public class FinalExercise01 {
public static void main(String[] args) {
Circle circle = new Circle(2);
System.out.println(circle.calArea());
}
}
class Circle {
private double radius;
private final double PII = 3.14;
public Circle(double radius) {
this.radius = radius;
}
public double calArea() {
return PII * radius * radius;
}
}
final修饰变量相当于常量,不能修改…
6. 抽象类
父类方法的不确定性
当父类的某些方法,需要声明,但是又不确定如何实现时,可以将其声明为抽象方法,那么这个方法就可以写为抽象类
1. 基本介绍
用abstract关键字修饰一个类时,这个类就叫抽象类。
访问修饰符 abstract 类名 {
}用abstract关键字来修饰一个方法时,这个方法就是抽象方法
访问修饰符 abstract 返回类型 方法名(参数列表);抽象类的价值更多是用于设计模式中,是设计者设计好后,让子类继承并实现抽象类()
2. 抽象类注意事项和使用细节
- 抽象类不能被实例化
- 抽象类不一定包含abstract方法,抽象类可以没有abstract方法
- 一个类一旦包含了abstract方法,则这个类一定要声明为abstract
- abstract只能修饰类和方法,不能修饰属性和其他的
- 抽象类可以有任意成员【抽象类还是类】
- 抽象方法不能有主体,不能实现
- 如果一个类继承了抽象类,则它必须实现抽象类的所有抽象方法,除非它自己也声明为abstract类
- 抽象方法不能使用private、final、static来修饰,因为这些关键字都是喝重写相违背的
3. 抽象类的最佳实践
- 编写方法 calculateTime(),可以计算某段代码的耗时时间。
- 编写抽象方法 job()。
- 编写两个子类AA和BB,继承抽象类Template,并实现job方法。
- 编写一个测试类main。
mian类
package com.qdu.abstract_;
public class Abstract02 {
public static void main(String[] args) {
AA aa = new AA();
aa.calculateTime();
BB bb = new BB();
bb.calculateTime();
}
}
template类
package com.qdu.abstract_;
abstract class Template {
public abstract void job();
public void calculateTime() {
long start = System.currentTimeMillis();
job();
long end = System.currentTimeMillis();
System.out.println("执行时间 " + (end - start));
}
}
AA类
package com.qdu.abstract_;
public class AA extends Template{
@Override
public void job() {
long sum = 0;
for (long i = 0; i <= 80000; i ++ ) {
sum += i;
}
}
}
BB类
package com.qdu.abstract_;
public class BB extends Template{
@Override
public void job() {
long sum = 0;
for (long i = 0; i <= 100000; i ++ ) {
sum += i;
}
}
}
7. 接口
1. 基本介绍
接口就是给出一些没有实现的方法,封装到一起,到某个类要使用的时候,根据具体的情况把这些方法写出来。
interface 接口名 {
//属性
//方法
}
class 类名 implements 接口 {
自己属性;
自己方法;
//必须实现接口的抽象方法
}
在jdk7.0前,接口里所有的方法都没有方法体,即都是抽象方法。
在jdk8.0后,接口可以有静态方法,默认方法(需要使用default关键字修饰),就是说接口中可以有方法的具体实现(static修饰)
2. 接口的使用细节和注意事项
- 接口不能被实例化
- 接口中所有的方法是public方法,接口中抽象方法,可以不用abstract修饰
- 一个普通类实现接口,就必须将该接口的所有方法都实现
- 抽象类实现接口,可以不用实现接口的方法
- 一个类同时可以实现多个接口
- 接口中的属性只能是final的,而且是public static final修饰符
- 接口中的属性的访问修饰:接口名:属性名
- 一个接口不能继承其它的类,但是可以继承多个别的接口
- 接口的修饰符只能是public和默认,这点和类的修饰符是一样的
3. 接口VS继承类
当子类继承了父类,就自动的拥有父类的功能
如果子类需要扩展功能,可以通过实现接口的方式扩展
可以理解实现接口是对java单继承机制的一种补充
接口和继承解决的问题不同
继承的价值主要在于:解决代码的复用性和可维护性
接口的价值主要在于:设计,设计好各种规范(方法),让其它类去实现这些方法,即更加的灵活…
接口比继承更加灵活
接口比继承更加灵活,继承是满足 is-a 的关系,而接口只需要满足 like-a 的关系
4. 接口的多态特性
- 多态参数:一个接口的参数可以接收不同的对象
- 多态数组:接口类型的数组
- 接口存在多态传递现象
8. 内部类
1. 基本介绍
一个类的内部又完整的嵌套了另一个类结构。被嵌套的类称为内部类,嵌套其他类的类称为外部类。是我们类的第五大成员【类的五大成员:属性、方法、构造器、代码块、内部类】,内部类的最大特点是可以直接访问私有属性,并且可以体现类与类之间的包含关心。
2. 基本语法
class Outer {//外部类
class Inner {//内部类
}
}
class Other {//外部其他类
}
3. 内部类的分类
定义在外部类局部位置上(比如方法内):
- 局部内部类(有类名)
- 匿名内部类(没有类名)
定义在外部类的成员位置上:
- 成员内部类(没有static修饰)
- 静态内部类(使用static修饰)
4. 局部内部类的使用
局部内部类是定义在外部类的局部位置,比如方法中,且有类名
- 可以直接方法外部类的所有成员,包括私有成员
- 不能添加访问修饰符,但是可以用final修饰
- 作用域:仅仅在定义它的方法或代码块中
- 局部内部类可以直接访问外部类的成员
- 外部类在方法中,可以创建局部内部类的对象,然后调用方法
- 外部其他类不能访问局部内部类
- 如果外部类和局部内部类的成员重名时,遵循就近原则,如果想访问外部类的成员,则可以使用(外部类名.this.成员)去访问。
package com.qdu.innerclass;
public class LocalInnerClass {
public static void main(String[] args) {
Outer outer = new Outer();
outer.m1();
}
}
class Outer {
private int n = 100;
private void m2() {
System.out.println("Outer m2()");
}
public void m1() {
final class Inner {
private int n = 800;
public void f1() {
System.out.println("n = " + n + "\n外部类的 n = " + Outer.this.n);
m2();
}
}
Inner inner = new Inner();
inner.f1();
}
}
5. 匿名内部类的使用
匿名内部类是定义在外部类的局部位置,比如方法中,并且没有类名
基本语法
new 类或接口(参数列表){
类体;
};
基于接口的匿名内部类
package com.qdu.innerclass;
public class AnonymousInnerClass {
public static void main(String[] args) {
Outer04 outer04 = new Outer04();
outer04.method();
}
}
class Outer04 {
private int n1 = 10;
public void method() {
IA tiger = new Tiger();
tiger.cry();
IA tiger1 = new IA(){
@Override
public void cry() {
System.out.println("老虎又叫唤...");
}
};
tiger1.cry();
}
}
interface IA {
public void cry();
}
class Tiger implements IA {
@Override
public void cry() {
System.out.println("老虎叫唤...");
}
}
class Father {
public Father(String name) {
super();
}
public void test() {
}
}
基于类的匿名内部类
Father father = new Father("jack") {
@Override
public void test() {
System.out.println("重写了Father的test方法");
}
};
father.test();
6. 使用细节和注意事项
- 匿名内部类的语法比较奇特,请大家注意,因为匿名内部类既是一个类的定义,同时它本身也是一个对象,因此从语法上看,它既有定义类的特征,也有创建对象的特征,对前面代码分析可以看出这个特点,因此可以调用匿名内部类方法。
Person person = new Person() {
@Override
public void h1() {
System.out.println("匿名内部类重写了 hi() 方法 ... ");
}
};
person.h1();
new Person() {
@Override
public void h1() {
System.out.println("匿名内部类重写了 hi() 方法 ... 哈哈哈哈哈哈!!!");
}
}.h1();
- 可以直接访问外部类的所有成员,包括私有成员
- 不能添加访问修饰符,因为它的地位就是一个局部变量
- 作用域:仅仅在定义它的方法或代码块中
- 匿名内部类访问外部成员【直接访问】
- 外部其他类不能访问匿名内部类(因为匿名内部类的地位是一个局部变量)
- 如果外部类和匿名内部类的成员重名时,匿名内部类访问的话,默认遵循就近原则,如果想访问外部类成员,则可以使用(外部类名.this.成员)去访问
7. 成员内部类
成员内部类是定义在外部类的成员位置,并且没有static修饰
- 可以直接访问外部类的所有成员,包含私有的。
- 可以添加任意的访问修饰符(public、protected、默认、private),因为它的地位就是一个成员。
- 作用域:和外部类的其他成员一样,为整个类体;在外部类的成员方法中创建成员内部类对象,再调用方法。
- 成员内部类 访问 外部类成员【直接访问】。
- 外部类 访问 成员内部类【创建对象,再访问】。
- 外部其他类 访问 成员内部类。
- 如果外部类和内部类的成员重名时,内部类访问的话,默认遵循就近原则,如果想访问外部类的成员,则可以使用(外部类名.this.成员)去访问。
8. 静态内部类
静态内部类是定义在外部类的成员位置,并且有static修饰
- 可以直接访问外部类的所有静态成员,包含私有的,但不能直接访问非静态成员。
- 可以添加任意访问修饰符(public、protected、默认、private),因为它的地位就是一个成员。
- 作用域:同其他的成员,为整个类体。
- 静态内部类 访问 外部类【直接访问】。
- 外部类 访问 静态内部类【先创建对象,再访问】。
- 其他外部类 访问 静态内部类。
- 如果外部类和静态内部类的成员重名时,静态内部类访问的时候,默认遵循就近原则,如果想访问外部类的成员,则可以使用(外部类名.成员)去访问。
我亦无他,唯手熟尔