Java基础编程(高级部分)

1. 类变量和类方法

1.1 什么是类变量

类变量也叫静态变量/静态属性,是该类的所有对象共享的变量,任何一个该类的对象去访问它时,取到的都是相同的值同样任何一个该类的对象去修改它时,修改的也是同一个变量。

1.2 定义类变量

1.3 访问类变量

类名.类变量名

或者 对象名.类变量名【静态变量的访问修饰符的访问权限和范围和普通属性是一样的。】

推荐使用: 类名.类变量名;
代码:

java 复制代码
package com.zakedu.static_;
 public class VisitStatic{
 public static void main(String[]args){
 //类名.类变量名
//说明:类变量是随着类的加载而创建,所以即使没有创建对象实例也可以访问
System.out.println(A.name);
 A a = new A();
 //通过对象名.类变量名
System.out.println("a.name="+a.name);
 } }

 class A{
 //类变量
//类变量的访问,必须遵守相关的访问权限.
 public static String name ="知昂可";
 //普通属性/普通成员变量/非静态属性/非静态成员变量/实例变量
private int num = 10;
}

1.4 类变量使用注意事项

  1. 什么时候需要用类变量

当我们需要让某个类的所有对象都共享一个变量时,就可以考虑使用类变量(静态变量):

比如:定义学生类,统计所有学生共交多少钱。Student (name, staticfee)

  1. 类变量与实例变量(普通属性)区别

类变量是该类的所有对象共享的,而实例变量是每个对象独享的。

  1. 加上static称为类变量或静态变量,否则称为实例变量/普通变量/非静态变量

  2. 类变量可以通过类名.类变量名或者对象名.类变量名来访问,但java设计者推荐我们使用类名.类变量名方式访问。【前提是满足访问修饰符的访问权限和范围】

  3. 实例变量不能通过类名.类变量名方式访问。

  4. 类变量是在类加载时就初始化了,也就是说,即使你没有创建对象,只要类加载了,就可以使用类变量了。

  5. 类变量的生命周期是随类的加载开始,随着类消亡而销毁。
    代码:

java 复制代码
package com.zakedu.static_;
public class StaticDetail{
 public static void main(String[]args){
 B b = new B();
 //System.out.println(B.n1);
 System.out.println(B.n2);
 //静态变量是类加载的时候,就创建了,所以我们没有创建对象实例
//也可以通过类名.类变量名来访问
System.out.println(C.address);
 }}

class B {
 public int n1 = 100;
 public static int n2 = 200;
 }
 class C {
 public static String address = "北京";
 }

1.5 类方法基本介绍

1.6 类方法的调用

1.7 类方法应用案例

java 复制代码
 package com.zakedu.static_;
 public class StaticMethod {
public static void main(String[] args) {
 //创建2个学生对象,叫学费
Stu tom = new Stu("tom");
 //tom.payFee(100);
 Stu.payFee(100);//对不对?对
Stu mary = new Stu("mary");
 //mary.payFee(200);
 Stu.payFee(200);//对
//输出当前收到的总学费
Stu.showFee();//300
 //如果我们希望不创建实例,也可以调用某个方法(即当做工具来使用)
 //这时,把方法做成静态方法时非常合适
System.out.println("9 开平方的结果是=" + Math.sqrt(9));
 System.out.println(MyTools.calSum(10, 30));
 } }
 //开发自己的工具类时,可以将方法做成静态的,方便调用
class MyTools {
 //求出两个数的和
public static double calSum(double n1, double n2) {
return n1 + n2;
 }
 //可以写出很多这样的工具方法...
 }
 class Stu {
 private String name;//普通成员
//定义一个静态变量,来累积学生的学费
private static double fee = 0;
 public Stu(String name) {
 this.name = name;
 }
 //说明
//1. 当方法使用了static修饰后,该方法就是静态方法
//2. 静态方法就可以访问静态属性/变量
public static void payFee(double fee) {
 Stu.fee += fee;//累积到
}
 public static void showFee() {
 System.out.println("总学费有:" + Stu.fee);
 }}

1.8 类方法经典的使用场景

1.9 类方法使用注意事项

  1. 类方法和普通方法都是随着类的加载而加载,将结构信息存储在方法区:

类方法中无this的参数

普通方法中隐含着this的参数

  1. 类方法可以通过类名调用,也可以通过对象名调用。

  2. 普通方法和对象有关,需要通过对象名调用,比如对象名.方法名(参数),不能通过类名调用。

  3. 类方法中不允许使用和对象有关的关键字,比如this和super。普通方法(成员方法)可以。

  4. 类方法(静态方法)中只能访问静态变量或静态方法。

  5. 普通成员方法,既可以访问非静态成员,也可以访问静态成员。
    小结: 静态方法,只能访问静态的成员,非静态的方法,可以访问静态成员和非静态成员(必须遵守访问权限)
    代码:

java 复制代码
 packagecom.zakedu.static_;
 public class StaticMethodDetail{
 public static void main(String[]args){
 D.hi();//ok
 //非静态方法,不能通过类名调用
//D.say();,错误,需要先创建对象,再调用
new D().say();//可以
} }

 class D {
 private int n1 = 100;
 private static int n2 = 200;
 public void say() {//非静态方法,普通方法
}

 public static void hi() {//静态方法,类方法
//类方法中不允许使用和对象有关的关键字,
//比如this 和 super。普通方法(成员方法)可以。
//System.out.println(this.n1);
 }

 //类方法(静态方法)中 只能访问 静态变量 或静态方法
//口诀:静态方法只能访问静态成员.
 public static void hello() {
 System.out.println(n2);
 System.out.println(D.n2);
 //System.out.println(this.n2);不能使用
hi();//OK
 //say();//错误
}

 //普通成员方法,既可以访问 非静态成员,也可以访问静态成员
//小结: 非静态方法可以访问 静态成员和非静态成员
public void ok() {
 //非静态成员
System.out.println(n1);
 say();
 //静态成员
System.out.println(n2);
 hello();
 } }

1.10 练习

看看下面代码有没有错误,如果有错误,就修改,看看输出什么?

代码1:

java 复制代码
class Person { //StaticExercise02.java 2min 时间
private int id;
private static int total =0;
 public static int getTotalPerson(){
 //id++;//错误,注销
return total;
 }
 public Person(){//构造器
total++; //total=1
 id=total;//id=1
 }}

 public class TestPerson{
 public static void main(String[]args){
 System.out.println("Numberoftotalis"+Person.getTotalPerson());//0
 Person p1 = new Person();
 System.out.println("Numberoftotalis"+Person.getTotalPerson());//1
 } }

代码2:

java 复制代码
 class Person{//StaticExercise03.java2min看
private int id;
 private static int total =0;
 public static void setTotalPerson(inttotal){
//this.total=total;//错误,因为在static方法中,不可以使用this关键字
Person.total=total;
 }
 public Person(){//构造器
total++;
 id =total;
 }}
 public class TestPerson{
 public static void main(String[]args){
 Person.setTotalPerson(3);
 new Person();//最后total的值就是4
 } }

//小结:记住两句话(1)静态方法,只能访问静态成员(2)非静态方法,可以访问所有的成员
//(3)在编写代码时,仍然要遵守访问权限规则

2. 理解main方法语法

2.1 深入理解main方法

提示:

  1. 在main()方法中,我们可以直接调用main方法所在类的静态方法或静态属性。 14

  2. 但是,不能直接访问该类中的非静态成员,必须创建该类的一个实例对象后,才能通过这个对象去访问类中的非静 态成员
    代码:

java 复制代码
package com.zakpedu.main_;
 public class Main01{
 //静态的变量/属性
private static String name="zak";
 //非静态的变量/属性
private int n1 = 10000;
 //静态方法
public static void hi(){
 System.out.println("Main01的hi方法");
}
 //非静态方法
public void cry() {
 System.out.println("Main01 的 cry 方法");
 }
 public static void main(String[] args) {
 //可以直接使用 name
 //1. 静态方法main 可以访问本类的静态成员
System.out.println("name=" + name);
 hi();
 //2. 静态方法main 不可以访问本类的非静态成员
//System.out.println("n1=" + n1);//错误
//cry();
 //3. 静态方法main 要访问本类的非静态成员,需要先创建对象 , 再调用即可
Main01 main01 = new Main01();
 System.out.println(main01.n1);//ok
 main01.cry();
 } }

案例演示:

3. 代码块

3.1 基本介绍

代码化块又称为初始化块,属于类中的成员[即是类的一部分],类似于方法,将逻辑语句封装在方法体中,通过{ }包围起来。

但和方法不同,没有方法名,没有返回,没有参数,只有方法体,而且不用通过对象或类显式调用,而是加载类时,或创建对象时隐式调用。

3.2 基本语法

代码:

java 复制代码
 package com.zakedu.codeblock_;
 public class CodeBlock01{
 public static void main(String[]args){
 Movie movie = new Movie("你好,李焕英");
 System.out.println("===============");
 Movie movie2 = new Movie("唐探3",100,"陈思诚");
 }}

class Movie {
 private String name;
 private double price;
 private String director;
 //3 个构造器-》重载
//解读
//(1) 下面的三个构造器都有相同的语句
//(2) 这样代码看起来比较冗余
//(3) 这时我们可以把相同的语句,放入到一个代码块中,即可
//(4) 这样当我们不管调用哪个构造器,创建对象,都会先调用代码块的内容
//(5) 代码块调用的顺序优先于构造器..
{
 System.out.println("电影屏幕打开...");
 System.out.println("广告开始...");
 System.out.println("电影正是开始...");
}

 public Movie(String name) {
 System.out.println("Movie(String name) 被调用...");
 this.name = name;
 }

 public Movie(String name, double price) 
this.name = name;
 this.price = price;
 }

 public Movie(String name, double price, String director) {
 System.out.println("Movie(String name, double price, String director) 被调用...");
 this.name = name;
 this.price = price;
 this.director = director;
 } }

3.3 代码块使用注意事项

java 复制代码
package com.zakedu.codeblock_;
 public class CodeBlockDetail01 {
 public static void main(String[] args) {
 //类被加载的情况举例
//1. 创建对象实例时(new)
 // AA aa = new AA();
 //2. 创建子类对象实例,父类也会被加载, 而且,父类先被加载,子类后被加载
// AA aa2 = new AA();
 //3. 使用类的静态成员时(静态属性,静态方法)
 // System.out.println(Cat.n1);
 //static 代码块,是在类加载时,执行的,而且只会执行一次.
// DDdd=newDD();
// DDdd1=newDD();
 //普通的代码块,在创建对象实例时,会被隐式的调用。
// 被创建一次,就会调用一次。
// 如果只是使用类的静态成员时,普通代码块并不会执行
System.out.println(DD.n1);//8888, 静态模块块一定会执行
}}

 class DD {
 public static int n1 = 8888;//静态属性
//静态代码块
  static {
     System.out.println("DD 的静态代码 1 被执行...");//
 }
 //普通代码块, 在new 对象时,被调用,而且是每创建一个对象,就调用一次
//可以这样简单的,理解 普通代码块是构造器的补充
{
    System.out.println("DD 的普通代码块...");
} }

 class Animal {
 //静态代码块
    static {
     System.out.println("Animal 的静态代码 1 被执行...");//
 } }

class Cat extends Animal {
 public static int n1 = 999;//静态属性
//静态代码块
    static {
     System.out.println("Cat 的静态代码 1 被执行...");//
 } }

 class BB {
 //静态代码块
    static {
     System.out.println("BB 的静态代码 1 被执行...");//1
 } }

class AAextends BB {
 //静态代码块
    static{
     System.out.println("AA的静态代码1被执行...");//2
 } }
java 复制代码
 package com.zakedu.codeblock_;
 public class CodeBlockDetail02{
 public static void main(String[]args){
     A a = new A();//(1)A静态代码块01(2)getN1被调用...(3)A普通代码块01(4)getN2被调用...(5)A()构造器被调用
} }

classA{
 {  //普通代码块
    System.out.println("A 普通代码块 01");
 }
 private int n2 = getN2();//普通属性的初始化

 static { //静态代码块
    System.out.println("A 静态代码块 01");
 }

 //静态属性的初始化
 private static int n1 = getN1();
 public static int getN1() {
     System.out.println("getN1 被调用...");
     return 100;
 }
 public int getN2() { //普通方法/非静态方法
     System.out.println("getN2 被调用...");
     return 200;
 }
 //无参构造器
 public A() {
     System.out.println("A() 构造器被调用");
 } }
java 复制代码
package com.zakedu.codeblock_;
 public class CodeBlockDetail03{
 public static void main(String[]args){
 new BBB();//(1)AAA的普通代码块(2)AAA()构造器被调用(3)BBB的普通代码块(4)BBB()构造器被调用
} }

 class AAA{//父类Object
 {
     System.out.println("AAA的普通代码块");
 }
 public AAA(){
 //(1)super()
 //(2)调用本类的普通代码块
    System.out.println("AAA()构造器被调用....");
 }}

class BBB extendsAAA {
 {
     System.out.println("BBB 的普通代码块...");
 }
 public BBB() {
 //(1)super()
 //(2)调用本类的普通代码块
    System.out.println("BBB() 构造器被调用....");
 } }
java 复制代码
package com.zakedu.codeblock_;
public class CodeBlockDetail04 {
 public static void main(String[] args) {
 //说明
//(1) 进行类的加载
//1.1 先加载 父类 A021.2 再加载 B02
 //(2) 创建对象
//2.1 从子类的构造器开始
//new B02();//对象
 new C02();
 } }

class A02 { //父类
private static int n1 = getVal01();
 static {
     System.out.println("A02 的一个静态代码块..");//(2)
 }

{
   System.out.println("A02 的第一个普通代码块..");//(5)
}

 public int n3 = getVal02();//普通属性的初始化
 public static int getVal01() {
     System.out.println("getVal01");//(1)
     return 10;
 }

 public int getVal02() {
     System.out.println("getVal02");//(6)
     return 10;
 }

public A02() {//构造器
    //隐藏
    //super()
     //普通代码和普通属性的初始化......
     System.out.println("A02 的构造器");//(7)
 } }

class C02 {
     private int n1 = 100;
     private static int n2 = 200;
     private void m1() {
     }
     private static void m2() {
     }

static {
    //静态代码块,只能调用静态成员
    //System.out.println(n1);错误
    System.out.println(n2);//ok
     //m1();//错误
    m2();
 }

{
 //普通代码块,可以使用任意成员
    System.out.println(n1);
     System.out.println(n2);//ok
     m1();
     m2();
}  }

class B02 extends A02 { //
     private static int n3 = getVal03();
     static {
     System.out.println("B02 的一个静态代码块..");//(4)
 }
     public int n5 = getVal04();
 {
     System.out.println("B02 的第一个普通代码块..");//(9)
 }

  publicstaticintgetVal03(){
     System.out.println("getVal03");//(3)
     return 10;
 }

public int getVal04(){
 System.out.println("getVal04");//(8)
 return 10;
 }
 //一定要慢慢的去品..
 public B02(){//构造器
//隐藏了
//super()
 //普通代码块和普通属性的初始化...
     System.out.println("B02的构造器");//(10)
 //TODOAuto-generatedconstructorstub
 } }

4. 单例设计模式

4.1 什么是设计模式

  1. 静态方法和属性的经典使用

  2. 设计模式是在大量的实践中总结和理论化之后优选的代码结构、编程风格、以及解决问题的思考方式。设计模式就像是经典的棋谱,不同的棋局,我们用不同的棋谱,免去我们自己再思考和摸索

4.2 什么是单例模式

单例(单个的实例)

  1. 所谓类的单例设计模式,就是采取一定的方法保证在整个的软件系统中,对某个类只能存在一个对象实例,并且该类只提供一个取得其对象实例的方法

  2. 单例模式有两种方式: 1)饿汉式 2)懒汉式

4.3 单例模式应用实例

演示饿汉式和懒汉式单例模式的实现。

步骤如下:

  1. 构造器私有化 =》防止直接new

  2. 类的内部创建对象

  3. 向外暴露一个静态的公共方法。getInstance
    代码:

java 复制代码
 package com.zakedu.single_;
 public class SingleTon01 {
    public static void main(String[] args) {
       //  GirlFriend xh = new GirlFriend("小红");
       //  GirlFriend xb = new GirlFriend("小白");
 //通过方法可以获取对象
     GirlFriend instance = GirlFriend.getInstance();
     System.out.println(instance);
     GirlFriend instance2 = GirlFriend.getInstance();
     System.out.println(instance2);
     System.out.println(instance == instance2);//T
     //System.out.println(GirlFriend.n1);
 } }

//有一个类, GirlFriend
 //只能有一个女朋友
class GirlFriend {
 private String name;
//public static int n1 = 100;
 //为了能够在静态方法中,返回 gf对象,需要将其修饰为static
 //對象,通常是重量級的對象, 餓漢式可能造成創建了對象,但是沒有使用.
 private static GirlFriend gf = new GirlFriend("小红红");

 //如何保障我们只能创建一个 GirlFriend 对象
//步骤[单例模式-饿汉式]
 //1. 将构造器私有化
//2. 在类的内部直接创建对象(该对象是static)
 //3. 提供一个公共的static方法,返回 gf对象
private GirlFriend(String name) {
 System.out.println("構造器被調用.");
 this.name = name;
 }

 public static GirlFriend getInstance() {
     return gf;
 }

 @Override
     public String toString() {
         return "GirlFriend{" +
         "name='" + name + '\' +
         '}';
 } }
java 复制代码
package com.zakedu.single_;
 /**
 * 演示懶漢式的單例模式
*/
 public class SingleTon02 {
     public static void main(String[] args) {
     //new Cat("大黃");
     //System.out.println(Cat.n1);
         Cat instance = Cat.getInstance();
         System.out.println(instance);
     //再次調用getInstance
         Cat instance2 = Cat.getInstance();
     System.out.println(instance2);
     System.out.println(instance == instance2);//T
 } }
 //希望在程序運行過程中,只能創建一個Cat對象
//使用單例模式
class Cat {
 private String name;
 public static int n1 = 999;
 private static Cat cat ; //默認是 null

 //步驟
//1.仍然構造器私有化
//2.定義一個static 靜態屬性對象
//3.提供一個public 的 static 方法,可以返回一個Cat對象
//4.懶漢式,只有當用戶使用getInstance 時,才返回cat對象, 後面再次調用時,會返回上次創建的cat對象
//從而保證了單例
private Cat(String name) {
     System.out.println("構造器調用...");
     this.name = name;
 }
 public static Cat getInstance() {
     if(cat == null) {//如果還沒有創建 cat 對象
     cat = new Cat("小可愛");
 }
     return cat;
 }

 @Override
 public String toString() {
    return "Cat{" +
     "name='" + name + '\' +
     '}';
 } }

4.4 饿汉式VS懒汉式

  1. 二者最主要的区别在于创建对象的时机不同:饿汉式是在类加载就创建了对象实例,而懒汉式是在使用时才创建。

  2. 饿汉式不存在线程安全问题,懒汉式存在线程安全问题。

  3. 饿汉式存在浪费资源的可能。因为如果程序员一个对象实例都没有使用,那么饿汉式创建的对象就浪费了,懒汉式是使用时才创建,就不存在这个问题。

  4. 在我们javaSE标准类中,java.lang.Runtime就是经典的单例模式。

5. final 关键

5.1 基本介绍

final 可以修饰类、属性、方法和局部变量,

在某些情况下.程序员可能有以下需求,就会使用到final:

  1. 当不希望类被继承时,可以用final修饰.

  2. 当不希望父类的某个方法被子类覆盖/重写(override)时,可以用final关键字修饰。

【案例演示:访问修饰符 final 返回类型 方法名】

  1. 当不希望类的的某个属性的值被修改,可以用final修饰.

【案例演示: public final double TAX_RATE=0.08】

  1. 当不希望某个局部变量被修改,可以使用final修饰

【案例演示: final double TAX RATE=0.08】
代码:

java 复制代码
 package com.zakedu.final_;
 public class Final01 {
     public static void main(String[] args) {
        E e=newE();
         //e.TAX_RATE = 0.09;
 } }

 //如果我们要求A类不能被其他类继承
//可以使用final 修饰 A类
final class A { }

//class B extends A {}

 class C {
     //如果我们要求hi不能被子类重写
     //可以使用final 修饰 hi方法
    public final void hi() {}
 }

class D extends C {
 //@Override
// public void hi() {
// }
// System.out.println("重写了 C 类的 hi 方法..");
}

 //当不希望类的的某个属性的值被修改,可以用final修饰
class E {
 public final double TAX_RATE = 0.08;//常量
}

 //当不希望某个局部变量被修改,可以使用final修饰
class F {
     public void cry() {
     //这时,NUM 也称为 局部常量
    final double NUM = 0.01;
     //NUM = 0.9;
     System.out.println("NUM=" + NUM);
 } }

5.2 final 使用注意事项

  1. final修饰的属性又叫常量,一般用XX XX X来命名

  2. final修饰的属性在定义时,必须赋初值,并且以后不能再修改,赋值可以在如下位置之一【选择一个位置赋初值即可】:

1.定义时:如public final double TAX_RATE=0.08;

2.在构造器中

3.在代码块中。

  1. 如果final修饰的属性是静态的,则初始化的位置只能是

1.定义时 2.在静态代码块不能在构造器中赋值。

  1. final类不能继承,但是可以实例化对象。

  2. 如果类不是final类,但是含有final方法,则该方法虽然不能重写,但是可以被继承。
    代码:

java 复制代码
package com.zakedu.final_;
 public class FinalDetail01 {
 public static void main(String[] args) {
     CC cc =new CC();
     new EE().cal();
 } }

class AA{
 /*
 1. 定义时:如 publicfinal doubleTAX_RATE=0.08;
 2. 在构造器中
3. 在代码块中
*/
 public final double TAX_RATE = 0.08;//1.定义时赋值
 public final double TAX_RATE2 ;
 public final double TAX_RATE3 ;
 public AA() {//构造器中赋值
    TAX_RATE2= 1.1;
 }
 {//在代码块赋值
    TAX_RATE3= 8.8;
 } }

 class BB {
/*
如果final 修饰的属性是静态的,则初始化的位置只能是
1 定义时 2 在静态代码块 不能在构造器中赋值。
*/
 public static final double TAX_RATE = 99.9;
 public static final double TAX_RATE2 ;
     static {
     TAX_RATE2= 3.3;
     }}

 //final 类不能继承,但是可以实例化对象
   final class CC { }

//如果类不是final 类,但是含有final方法,则该方法虽然不能重写,但是可以被继承 即,仍然遵守继承的机制.

class DD {
 public final void cal() {
     //如果类不是final 类,但是含有final方法,则该方法虽然不能重写,但是可以被继承
    System.out.println("cal()方法");
 } }

 class EE extends DD { }
  1. 一般来说,如果一个类已经是final类了,就没有必要再将方法修饰成final方法。

  2. final不能修饰构造方法(即构造器)

  3. final和static往往搭配使用,效率更高,不会导致类加载.底层编译器做了优化处理。

class Demo{

public static final int i=16;

static{

System.out.println("zak"); } }

  1. 包装类(Integer,Double,Float,Boolean等都是final),String也是final类。
    代码:
java 复制代码
 packagecom.zakedu.final_;
 public class FinalDetail02{
   public static void main(String[]args){
     System.out.println(BBB.num);
 //包装类,String是final类,不能被继承
} }

//final和static往往搭配使用,效率更高,不会导致类加载.底层编译器做了优化处理
class BBB{
 public final static int num = 10000;
 static{
     System.out.println("BBB静态代码块被执行");
 }
}
 final class AAA{
 //一般来说,如果一个类已经是final类了,就没有必要再将方法修饰成final方法
//public final void cry(){}
 }

6. 抽象类

6.1 抽象类的介绍

  1. 用abstract关键字来修饰一个类时.这个类就叫抽象类

访问修饰符 abstract 类名{ }

  1. 用abstract关键字来修饰一个方法时,这个方法就是抽象方法

访问修饰符 abstract 返回类型 方法名(参数列表); //没有方法体

  1. 抽象类的价值更多作用是在于设计,是设计者设计好后,让子类继承并实现抽象类()

  2. 抽象类,是考官比较爱问的知识点,在框架和设计模式使用较多

6.2 抽象类使用的注意事项

  1. 抽象类不能被实例化

  2. 抽象类不一定要包含abstract方法。也就是说,抽象类可以没有abstract方法

  3. 一旦类包含了abstract方法,则这个类必须声明为abstract

  4. abstract只能修饰类和方法,不能修饰属性和其它的。
    代码:

java 复制代码
 package com.zakedu.abstract_;
 public class AbstractDetail01 {
 public static void main(String[] args) {
     //抽象类,不能被实例化
    //new A();
 } }
     //抽象类不一定要包含abstract方法。也就是说,抽象类可以没有abstract方法
    //,还可以有实现的方法。
abstract class A {
     public void hi() {
     System.out.println("hi");
 } }
 //一旦类包含了abstract 方法,则这个类必须声明为abstract
 abstract class B {
     public abstract void hi();
 }
     //abstract 只能修饰类和方法,不能修饰属性和其它的
class C {
     // public abstract int n1 = 1;
 }
  1. 抽象方法不能使用private、final和 static来修饰,因为这些关键字都是和重写相违背的。
    代码:
java 复制代码
 package com.zakedu.abstract_;
 public class AbstractDetail02 {
 public static void main(String[] args) {
 System.out.println("hello");
 } }
//抽象方法不能使用private、final 和 static 来修饰,因为这些关键字都是和重写相违背的
abstract class H {
 public  abstract void hi();//抽象方法
 }

//如果一个类继承了抽象类,则它必须实现抽象类的所有抽象方法,除非它自己也声明为abstract类
abstract class E {
     public abstract void hi();
 }
     abstract class F extends E {
 }

 class G extends E {
     @Override
     public void hi() { //这里相等于 G 子类实现了父类E的抽象方法,所谓实现方法,就是有方法体
} }

//抽象类的本质还是类,所以可以有类的各种成员
abstract class D {
     public int n1 = 10;
     public static String name = "zak";
     public void hi() {
     System.out.println("hi");
 }
   public abstract void hello();
   public static void ok() {
     System.out.println("ok");
 } }

7. 抽象类最佳实践-模板设计模式

7.1 基本介绍

抽象类体现的就是一种模板模式的设计,抽象类作为多个子类的通用模板,子类在抽象类的基础上进行扩展、改造,但子类总体上会保留抽象类的行为方式。
模板设计模式能解决的问题:

  1. 当功能内部一部分实现是确定,一部分实现是不确定的。这时可以把不确定的部分暴露出去,让子类去实现。

  2. 编写一个抽象父类,父类提供了多个子类的通用方法,并把一个或多个方法留给其子类实现,就是一种模板模式。

7.2 最佳实践

需求:

  1. 有多个类,完成不同的任务job

  2. 要求统计得到各自完成任务的时间

java编程建议:

1.先用最容易想到的方法-》代码实现 2.分析问题,提出使用模板设计模式

代码:

java 复制代码
 packagecom.zakedu.abstract_;
 abstract public class Template{ //抽象类-模板设计模式
    public abstract void job(); //抽象方法
    public void calculateTime(){ //实现方法,调用job方法
    //得到开始的时间
    long start = System.currentTimeMillis();
    job();//动态绑定机制
    //得的结束的时间
    long end = System.currentTimeMillis();
     System.out.println("任务执行时间"+(end-start));
 } }
java 复制代码
 packagecom.zakedu.abstract_;
 public class AA extends Template{
     //计算任务
    //1+....+800000
     @Override
 public void job(){//实现Template的抽象方法job
     long num=0;
     for(longi=1;i<=800000;i++){
     num +=i;
 } }

//publicvoidjob2(){
 // 得到开始的时间
// long start = System.currentTimeMillis();
 // long num =0;
//for (long i = 1; i <= 200000; i++) {
// num += i;
// }
 //得的结束的时间
// long end = System.currentTimeMillis();
// System.out.println("AA 执行时间 " + (end- start));
// } 
}
java 复制代码
 package com.zakedu.abstract_;
 public class BB extends Template{
     public void job() {//这里也去,重写了 Template 的 job 方法
        long num = 0;
         for (long i = 1; i <= 80000; i++) {
         num *= i;
 } } }
java 复制代码
package com.zakedu.abstract_;
 public class TestTemplate {
     public static void main(String[] args) {
        AA aa = new AA();
         aa.calculateTime(); //这里还是需要有良好的 OOP 基础,对多态
         BB bb = new BB();
         bb.calculateTime();
 } }

8. 接口

8.1 基本介绍

8.2 注意事项

代码:

java 复制代码
packagecom.zakedu.interface_;
 public class InterfaceDetail01{
     public static void main(String[]args){
     //new IA();
 } }

 //1.接口不能被实例化
//2.接口中所有的方法是 public方法, 接口中抽象方法,可以不用abstract 修饰
//3.一个普通类实现接口,就必须将该接口的所有方法都实现,可以使用alt+enter来解决
//4.抽象类去实现接口时,可以不实现接口的抽象方法
interface IA {
     void say();//修饰符 public protected 默认 private
     void hi();
 }
 class Cat implements IA{
     @Override
     public void say() {
 }

 @Override
     public void hi() {
 } }

 abstract class Tiger implements IA {
 }

代码:

java 复制代码
packagecom.zakedu.interface_;
 public class InterfaceDetail02{
 public static void main(String[]args){
 //证明接口中的属性,是public static final
     System.out.println(IB.n1);//说明n1就是static
 //IB.n1=30;说明n1是final
 } }

 interface IB{
 //接口中的属性,只能是final的,而且是publicstaticfinal修饰符
    int n1 = 10; //等价publicstaticfinalintn1=10;
     void hi();
     }
  interfaceIC{
     void say();
  } 
//接口不能继承其它的类,但是可以继承多个别的接口
interface ID extends IB,IC {
 }

 //接口的修饰符 只能是 public 和默认,这点和类的修饰符是一样的
interface IE{ }

 //一个类同时可以实现多个接口
class Pig implements IB,IC {
     @Override
     public void hi() {
 }
     @Override
     public void say() {
 } }

练习:

8.3 实现接口vs继承类

代码:

java 复制代码
packagecom.zakedu.interface_;
 public class ExtendsVsInterface{
  public static void main(String[]args){
     LittleMonkey wuKong = new LittleMonkey("悟空");
     wuKong.climbing();
     wuKong.swimming();
     wuKong.flying();
 } }

 //猴子
class Monkey{
private String name;
     public Monkey(String name) {
     this.name = name;
 }
 public void climbing() {
     System.out.println(name + " 会爬树...");
 }
 public String getName() {
 return name;
 } }
 //接口
interface Fishable {
 void swimming();
 }
 interface Birdable {
 void flying();
 }

 //继承
//小结: 当子类继承了父类,就自动的拥有父类的功能
//如果子类需要扩展功能,可以通过实现接口的方式扩展.
//可以理解 实现接口 是 对java 单继承机制的一种补充.

class LittleMonkey extends Monkey implements Fishable,Birdable {
     public LittleMonkey(String name) {
     super(name);
 }
     @Override
         public void swimming() {
         System.out.println(getName() + " 通过学习,可以像鱼儿一样游泳...");
 }
     @Override
         public void flying() {
         System.out.println(getName() + " 通过学习,可以像鸟儿一样飞翔...");
 } }

8.4 接口的多态特性

代码:

java 复制代码
package com.zakedu.interface_;
 public class InterfacePolyParameter{
     public static void main(String[]args){
     //接口的多态体现
    //接口类型的变量if01可以指向实现了IF接口类的对象实例
    IF if01 = new Monster();
       if01 = new Car();
 //继承体现的多态
//父类类型的变量a可以指向继承AAA的子类的对象实例
    AAA a=new BBB();
     a = new CCC();
 } }
interface IF{ }
     class Monster implements IF{}
     class Car implements IF{}
     class AAA{ }
 class BBB extends AAA{ }
 class CCC extends AAA{ }
java 复制代码
package com.zakedu.interface_;
 public class InterfacePolyArr{
     publicstaticvoidmain(String[]args){
     //多态数组->接口类型数组
        Usb[ ] usbs = new Usb[2];
        usbs[0] = new Phone_();
        usbs[1] = newCamera_();
 /*
给Usb数组中,存放Phone和相机对象,Phone类还有一个特有的方法call(),
请遍历Usb数组,如果是Phone对象,除了调用Usb接口定义的方法外,
还需要调用Phone特有方法call
 */
 for(inti=0;i<usbs.length;i++){
    usbs[i].work();//动态绑定..
     //和前面一样,我们仍然需要进行类型的向下转型
    if(usbs[i] instanceof Phone_) {//判断他的运行类型是 Phone_
     ((Phone_) usbs[i]).call();
 }} } }

 interface Usb{
     void work();
 }
 class Phone_ implements Usb {
     public void call() {
     System.out.println("手机可以打电话...");
 }
     @Override
     public void work() {
 }
     System.out.println("手机工作中...");
 }
 class Camera_ implements Usb {
     @Override
public void work(){
 System.out.println("相机工作中...");
 } }
java 复制代码
 package com.zakedu.interface_;
 /**
 *演示多态传递现象
*/
 public class InterfacePolyPass{
     public static void main(String[]args){
 //接口类型的变量可以指向,实现了该接口的类的对象实例
    IG ig = new Teacher();
 //如果IG继承了IH接口,而Teacher类实现了IG接口
//那么,实际上就相当于Teacher类也实现了IH接口.
 //这就是所谓的接口多态传递现象.
 IH ih = new Teacher();
 } }
 interface IH{
 void hi();
 }
 interface IG extends IH{}
 class Teacher implements IG{
    @Override
     public void hi(){
 } }

9. 内部类

如果定义类在局部位置(方法中/代码块):(1) 局部内部类 (2) 匿名内部类

定义在成员位置 (1) 成员内部类 (2) 静态内部类

9.1 基本介绍

一个类的内部又完整的嵌套了另一个类结构。被嵌套的类称为内部类(inner class),嵌套其他类的类称为外部类(outer class)。是我们类的第五大成员【思考:类的五大成员是哪些?[属性、方法、构造器、代码块、内部类]】,内部类最大的特点就是可以直接访问私有属性,并且可以体现类与类之间的包含关系,注意:内部类是学习的难点,同时也是重点,后面看底层源码时,有大量的内部类.

9.2 基本语法

9.3 内部类的分类

定义在外部类局部位置上(比如方法内):

  1. 局部内部类(有类名)

  2. 匿名内部类(没有类名,重点!!!!!!!!)

定义在外部类的成员位置上:

  1. 成员内部类(没用static修饰)

  2. 静态内部类(使用static修饰)

9.3.1 局部内部类的使用

代码:

java 复制代码
 package com.zakedu.innerclass;
 /**
 * 演示局部内部类的使用
*/
 public class LocalInnerClass {//
     public static void main(String[] args) {
     //演示一遍
        Outer02 outer02 = new Outer02();
        outer02.m1();
        System.out.println("outer02 的 hashcode=" + outer02);
 } }

class Outer02 {//外部类
    private int n1 = 100;
    private void m2() {
         System.out.println("Outer02 m2()");
 }//私有方法
public void m1() {//方法
    //1.局部内部类是定义在外部类的局部位置,通常在方法
    //3.不能添加访问修饰符,但是可以使用final 修饰
    //4.作用域 : 仅仅在定义它的方法或代码块中
final class Inner02 {//局部内部类(本质仍然是一个类)
     //2.可以直接访问外部类的所有成员,包含私有的
private int n1 = 800;
 public void f1() {
     //5. 局部内部类可以直接访问外部类的成员,比如下面 外部类n1 和 m2()
     //7. 如果外部类和局部内部类的成员重名时,默认遵循就近原则,如果想访问外部类的成员,
     //   使用 外部类名.this.成员)去访问
     //  解读 Outer02.this 本质就是外部类的对象, 即哪个对象调用了m1,Outer02.this就是哪个对象
    System.out.println("n1=" + n1 + " 外部类的 n1=" + Outer02.this.n1);
    System.out.println("Outer02.this hashcode=" + Outer02.this);
    m2();
 } }
     //6. 外部类在方法中,可以创建Inner02对象,然后调用方法即可
Inner02 inner02 = new Inner02();
 inner02.f1();
 } }

9.3.2 匿名内部类的使用(重要!!!!!!!)

代码:

java 复制代码
package com.zakedu.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() {//方法
    //基于接口的匿名内部类
    //解读
    //1.需求: 想使用IA接口,并创建对象
    //2.传统方式,是写一个类,实现该接口,并创建对象
    //3.需求是 Tiger/Dog 类只是使用一次,后面再不使用
    //4. 可以使用匿名内部类来简化开发
    //5. tiger 的编译类型 ?IA
     //6. tiger 的运行类型 ? 就是匿名内部类 Outer04$1
     /*
        我们看底层 会分配 类名 Outer04$1
    class Outer04$1 implements IA {
         @Override
         public void cry() {
         System.out.println("老虎叫唤...");
     } }
     */
 //7. jdk 底层在创建匿名内部类 Outer04$1,立即马上就创建了 Outer04$1实例,并且把地址
    //返回给 tiger
 //8. 匿名内部类使用一次,就不能再使用
    IA tiger = new IA() {
     @Override
     public void cry() {
     System.out.println("老虎叫唤...");
 } };
 System.out.println("tiger 的运行类型=" + tiger.getClass());
 tiger.cry();
 tiger.cry();
 tiger.cry();

// IAtiger = new Tiger();
// tiger.cry();
// 演示基于类的匿名内部类
// 分析
//1. father 编译类型 Father
 //2. father 运行类型 Outer04$2
 //3. 底层会创建匿名内部类
/*
 class Outer04$2 extends Father{
     @Override
     public void test() {
     System.out.println("匿名内部类重写了 test 方法");
 }}
 */
 //4. 同时也直接返回了 匿名内部类 Outer04$2的对象
//5. 注意("jack") 参数列表会传递给 构造器
Father father = new Father("jack"){
     @Override
     public void test() {
        System.out.println("匿名内部类重写了 test 方法");
}};
 System.out.println("father 对象的运行类型=" + father.getClass());//Outer04$2
father.test();
 //基于抽象类的匿名内部类
    Animal animal = newAnimal(){
         @Override
void eat() {
 System.out.println("小狗吃骨头...");
 } };
 animal.eat();
 } }

 interface IA {//接口
    public void cry();
 }


//class Tiger implements IA {
 //}
 //@Override
// public void cry() {
// }
// System.out.println("老虎叫唤...");
// class Dog implements IA{
// @Override
 //public void cry() {
// System.out.println("小狗汪汪...");
 //}

 class Father {//类
    public Father(String name) {//构造器
    System.out.println("接收到 name=" + name);
 }
 public void test() {//方法
}}
 abstract class Animal { //抽象类
    abstract void eat();
 }

代码:

java 复制代码
 package com.zakedu.innerclass;
 public class AnonymousInnerClassDetail {
    public static void main(String[] args) {
     Outer05 outer05 = new Outer05();
     outer05.f1();
     //外部其他类---不能访问----->匿名内部类
    System.out.println("main outer05 hashcode=" + outer05);
 } }
class Outer05 {
 private int n1 = 99;
 public void f1() {
     //创建一个基于类的匿名内部类
    //不能添加访问修饰符,因为它的地位就是一个局部变量
    //作用域 : 仅仅在定义它的方法或代码块中
Person p = new Person(){
     private int n1 = 88;
     @Override
     public void hi() {
     //可以直接访问外部类的所有成员,包含私有的
    //如果外部类和匿名内部类的成员重名时,匿名内部类访问的话,
    //默认遵循就近原则,如果想访问外部类的成员,则可以使用 (外部类名.this.成员)去访问
    System.out.println("匿名内部类重写了 hi 方法 n1=" +n1 +
                     " 外部内的n1="+Outer05.this.n1 );
     //Outer05.this 就是调用 f1 的 对象
    System.out.println("Outer05.this hashcode=" + Outer05.this);
    } };
     p.hi();//动态绑定, 运行类型是 Outer05$1
     //也可以直接调用, 匿名内部类本身也是返回对象
    // class 匿名内部类 extends Person {}

// new Person(){
// @Override
// public void hi() {
 //System.out.println("匿名内部类重写了 hi 方法,哈哈...");
// }
// @Override
// public void ok(String str) {
// super.ok(str);
// }
// }.ok("jack");
 } }
 class Person {//类
    public void hi() {
     System.out.println("Person hi()");
 }
 public void ok(String str) {
    System.out.println("Personok()"+str);
 } }
 //抽象类/接口...

9.3.3 匿名内部类的最佳实践

当做实参直接传递,简洁高效

java 复制代码
package com.zakedu.innerclass;
 import com.zakedu.abstract_.AA;
 public class InnerClassExercise01{
 public static void main(String[]args){
     //当做实参直接传递,简洁高效
    f1(newIL(){
         @Override
     publicvoidshow(){
     System.out.println("这是一副名画~~...");
 }});
     //传统方法
    f1(newPicture());
 }
//静态方法,形参是接口类型
public static void f1(IL il){
     il.show();
 } }

//接口
interfaceIL{
     voidshow();
 }
 //类->实现IL=>编程领域(硬编码)
 class Picture implements IL{
     @Override
     public void show(){
     System.out.println("这是一副名画XX...");
 } }

9.3.4 成员内部类的使用

  1. 作用域

和外部类的其他成员一样,为整个类体比如前面案例,在外部类的成员方法中创建成员内部类对象,再调用方法。

  1. 成员内部类---访问---->外部类成员(比如:属性) [访问方式:直接访问]

  2. 外部类---访问------>成员内部类(说明) 访问方式:创建对象,再访问

  3. 外部其他类---访问---->成员内部类

  4. 如果外部类和内部类的成员重名时,内部类访问的话。默认遵循就近原则,如果想访问外部类的成员,则可以使用(外部类名.this.成员)去访问
    代码:

java 复制代码
package com.zakedu.innerclass;
 public class MemberInnerClass01 {
    public static void main(String[] args) {
         Outer08 outer08 = new Outer08();
         outer08.t1();
 //外部其他类,使用成员内部类的三种方式
 //解读
 // 第一种方式
 // outer08.new Inner08(); 相当于把 new Inner08()当做是 outer08 成员
 // 这就是一个语法,不要特别的纠结.
     Outer08.Inner08 inner08 = outer08.new Inner08();
     inner08.say();
 // 第二方式 在外部类中,编写一个方法,可以返回 Inner08对象
    Outer08.Inner08 inner08Instance = outer08.getInner08Instance();
     inner08Instance.say()
}}

class Outer08 { //外部类
 private int n1 = 10;
     public String name = "张三";
     private void hi() {
     System.out.println("hi()方法...");
 }
//1.注意: 成员内部类,是定义在外部内的成员位置上
//2.可以添加任意访问修饰符(public、protected 、默认、private),因为它的地位就是一个成员

 public class Inner08 {//成员内部类
 private double sal = 99.8;
 private int n1 = 66;
 public void say() {
 //可以直接访问外部类的所有成员,包含私有的
//如果成员内部类的成员和外部类的成员重名,会遵守就近原则.
 //,可以通过 外部类名.this.属性 来访问外部类的成员
    System.out.println("n1 = " + n1 + " name = " + name + " 外部类的 n1=" + Outer08.this.n1);
 hi();
 } }
 //方法,返回一个Inner08实例
    public Inner08 getInner08Instance(){
     return new Inner08();
 }
 //写方法
    public void t1() {
 //使用成员内部类
//创建成员内部类的对象,然后使用相关的方法
    Inner08 inner08 = new Inner08();
     inner08.say();
    System.out.println(inner08.sal);
 } }

9.3.5 静态内部类的使用

说明:静态内部类是定义在外部类的成员位置,并且有static修饰

  1. 可以直接访问外部类的所有静态成员,包含私有的,但不能直接访问非静态成员

  2. 可以添加任意访问修饰符(public、 protected、默认、private),因为它的地位就是一个成员。

  3. 作用域:同其他的成员,为整个类体

  4. 静态内部类---访问---->外部类(比如:静态属性)[访问方式:直接访问所有静态成员]

  5. 外部类---访问------>静态内部类访问方式:创建对象,再访问

  6. 外部其他类---访问----->静态内部类

  7. 如果外部类和静态内部类的成员重名时,静态内部类访问的时,默认遵循就近原则。如果想访问外部类的成员,则可以使用(外部类名.成员)去访问
    代码:

java 复制代码
 package com.zakedu.innerclass;
  public class StaticInnerClass01{
     public static void main(String[]args){
     Outer10 outer10 = new Outer10();
     outer10.m1();
     //外部其他类使用静态内部类
    //方式1
     //因为静态内部类,是可以通过类名直接访问(前提是满足访问权限)
     Outer10.Inner10 inner10 = new Outer10.Inner10();
     inner10.say();
     //方式2
     //编写一个方法,可以返回静态内部类的对象实例.
     Outer10.Inner10 inner101 = outer10.getInner10();
     System.out.println("============");
     inner101.say();
     Outer10.Inner10 inner10_ = Outer10.getInner10_();
     System.out.println("************");
     inner10_.say();
  } }

class Outer10 { //外部类
    private int n1 = 10;
     private static String name = "张三";
     private static void cry() {}
     //Inner10 就是静态内部类
    //1. 放在外部类的成员位置
    //2. 使用static 修饰
    //3. 可以直接访问外部类的所有静态成员,包含私有的,但不能直接访问非静态成员
    //4. 可以添加任意访问修饰符(public、protected 、默认、private),因为它的地位就是一个成员
    //5. 作用域 :同其他的成员,为整个类体
    static class Inner10 {
     private static String name = "zak";
     public void say() {
     //如果外部类和静态内部类的成员重名时,静态内部类访问的时,
    //默认遵循就近原则,如果想访问外部类的成员,则可以使用 (外部类名.成员)
    System.out.println(name + " 外部类 name= " + Outer10.name);
     cry();
 } }

 public void m1() { //外部类---访问------>静态内部类 访问方式:创建对象,再访问
    Inner10 inner10 = new Inner10();
     inner10.say();
 }

public Inner10 getInner10() {
 return new Inner10();
 }

public static Inner10 getInner10_() {
 return new Inner10();
 } }
相关推荐
芒果披萨1 分钟前
El表达式和JSTL
java·el
q567315232 分钟前
在 Bash 中获取 Python 模块变量列
开发语言·python·bash
许野平27 分钟前
Rust: 利用 chrono 库实现日期和字符串互相转换
开发语言·后端·rust·字符串·转换·日期·chrono
也无晴也无风雨30 分钟前
在JS中, 0 == [0] 吗
开发语言·javascript
狂奔solar39 分钟前
yelp数据集上识别潜在的热门商家
开发语言·python
duration~42 分钟前
Maven随笔
java·maven
zmgst1 小时前
canal1.1.7使用canal-adapter进行mysql同步数据
java·数据库·mysql
跃ZHD1 小时前
前后端分离,Jackson,Long精度丢失
java
blammmp1 小时前
Java:数据结构-枚举
java·开发语言·数据结构
何曾参静谧1 小时前
「C/C++」C/C++ 指针篇 之 指针运算
c语言·开发语言·c++