Java第十章:面向对象高级

Java第十章:面向对象高级

类变量和类方法

类变量的定义和访问

定义:
	类变量也叫静态变量/静态属性,是该类的所有对象共享的变量。
	访问修饰符 static 数据类型 变量名;(推荐)
	static 访问修饰符 数据类型 变量名;
访问:
	类名.类变量名(推荐)
	对象名.类变量名
	前提:满足访问修饰符的访问权限和范围

类变量细节

1.类变量是在类加载时就初始化了,因此,即使你没有创建对象,也可访问类变量
2.类变量的生命周期是随类的加载开始,随类消亡而销毁

类变量内存布局

根据JDK版本不同:
	存储在类加载时反射生成的class原型对象的尾部,而class原型对象存在堆结构中
	存储在方法区的静态域中

类变量内存布局

类方法的定义和调用

定义:
	访问修饰符 static 数据返回类型 方法名(){ }(推荐)
	static 访问修饰符 数据返回类型 方法名(){ }
调用:
	类名.类方法名
	对象名.类方法名
	前提:满足访问修饰符的访问权限和范围

类方法细节

1.类方法和普通方法都是随着类的加载而加载,将结构信息存储在方法区;
  类方法中无this的参数
  普通方法中隐含着this的参数(因为类方法无法访问非静态成员,静态成员就是this的参数)
2.类方法中不允许使用和对象有关的关键字,如this和super,普通方法可以
3.静态方法,只能访问静态成员
  非静态方法,可以访问静态成员和非静态成员(必须遵守访问权限)

main方法

main方法语法:

形式: public static void main(String[] args)
1.java虚拟机需要调用类的main方法,所以该方法的访问权限是public
2.java虚拟机在执行main方法时不必创建对象,所以该方法必须是static
3.该方法接受String类型的数组参数,该数组中保存执行java命令时传递给所运行的类的参数
4.java 执行的程序 参数1 参数2 参数3

main方法细节

1.在main方法中,可以直接调用main方法所在类的静态成员
  但是不能直接访问该类中的非静态成员,必须创建该类的一个实例对象后,才能通过该对象去访问类中的非静态成员

IDEA中main方法传递参数

idea中main方法传递参数

代码块

代码块介绍和语法:

基本介绍:
	代码块又称为初始化块,属于类中的成员(即 是类的一部分),类似于方法,将逻辑语句封装在方法体中,通过{}包围起来。
	但和方法不同,没有方法名,没有返回,没有参数,只有方法体,而且不用通过对象或类显式调用,而是加载类时,或创建对象时隐式调用
基本语法:
	[修饰符]{
		代码
	};
	注意:
		1.修饰符可选,写只能写static
		2.代码块分为静态代码块和非静态代码块/普通代码块
		3.;可加可不加

代码块好处:

1.相当于另外一种形式的构造器(对构造器的补充机制),可以做初始化的操作
2.如果多个构造器中都有重复的语句,可以抽取到初始化块中,提高代码的重用性

代码块细节:

1.static代码块是类加载时,执行,只会执行一次;
2.普通代码块是创建对象时调用的,创建一次,调用一次
3.类加载的3种情况:
				1):创建对象实例时(new)
				2):创建子类对象实例,父类也会被加载
				3):使用类的静态成员时
4.创建一个对象时,在一个类 调用顺序是:
	1):加载类信息,同时调用静态代码块和静态属性初始化(优先级一样,按定义顺序调用)
	2):调用构造方法,而构造方法隐含 super() 和 普通代码块调用和普通属性初始化
	3):调用super()和调用普通代码块和普通属性的初始化(优先级一样,按定义顺序调用)
	4):执行构造方法剩余部分
5.创建一个子类对象时,调用顺序:
	1):先加载父类,父类的静态代码块和静态属性初始化
	2):后加载子类,子类的静态代码块和静态属性初始化
	3):调用构造方法,而先调用隐含的super后调用普通代码块和普通属性初始化,所以!
	4):父类的普通代码块和普通属性初始化
	5):父类构造方法的剩余部分
	6):子类的普通代码块和普通属性初始化
	7):子类构造方法的剩余部分
6.静态代码块只能直接调用静态成员,普通代码块可以调用任意成员。

代码块练习

代码块练习1

单例模式

单例模式

定义:采取一定的方法保证在整个的软件系统中,对某个类只能存在一个对象实例,并且该类只提供一个取得其对象实例的方法

饿汉式

package single_;

public class E_Han {
    public static void main(String[] args) {
        Girlfriend gf = Girlfriend.getInstance();
        Girlfriend gf1 = Girlfriend.getInstance();
        System.out.println(gf);
        System.out.println(gf1 == gf);//指向同一个对象,因为对象为static,类加载时就创建,且只创建一次
        //为什么叫饿汉式?因为加载即创建该实例,即使没用到也会创建
        System.out.println(Girlfriend.n1);
    }
}
class Girlfriend{
    private String name;

    public static int n1 = 100;

    //如何保障我们只能创建一个Girlfriend对象
    //步骤[单例模式-饿汉式]
    //1.将构造器私有化
    //2.在类的内部直接创建对象(该对象是static,方便后续静态方法返回该对象)
    //3.提供一个公共的static方法,返回girlfriend对象
    private Girlfriend(String name) {
        this.name = name;
    }
    private static Girlfriend girlfriend = new Girlfriend("刘浩存");
    public static Girlfriend getInstance(){
        return girlfriend;
    }

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

懒汉式

package single_;

public class Lan_Han {
    public static void main(String[] args) {
        Love mylove = Love.getInstance();
        Love mylove1 = Love.getInstance();
        System.out.println(mylove);
        System.out.println(mylove == mylove1);
        //为什么叫懒汉式?因为getInstance才创建,加载类时只创建了对象的引用变量
        System.out.println(Love.n1);
    }
}
class Love{
    private String name;
    public static int n1 = 100;

    private Love(String name) {
        this.name = name;
    }
    private static Love mylove;
    public static Love getInstance(){
        if(mylove == null){
            mylove = new Love("刘浩存");
        }
        return mylove;
    }

    @Override
    public String toString() {
        return "Love{" +
                "name='" + name + '\'' +
                '}';
    }
    //1.将构造器私有化
    //2.在类内部创建对象的引用变量,而非直接创建对象
    //3.提供一个公共的方法创建对象并返回
}

饿汉式和懒汉式区别

1.创建对象的时机不同:
	饿汉式在类加载时创建对象实例
	而懒汉式在使用时才创建
2.缺点不同:
	懒汉式存在线程安全问题
	而饿汉式存在浪费资源的可能

final关键字

final基本介绍

final可修饰:类、属性、方法和局部变量
使用情形:
1.不希望类被继承时
2.不希望父类方法被重写时
3.不希望类的某个属性的值被修改时
4.不希望某个局部变量被修改时

final细节

1.final修饰的属性又叫常量,一般 用XX_XX_XX来命名
2.final修饰的属性在定义时,必须初始化
	修饰普通属性:
		初始化位置:
			(1):定义时直接初始化
			(2):在构造器中
			(3):在代码块中
	修饰静态属性:
		初始化位置:
			(1):定义时直接初始化
			(2):在静态代码块中,不能在构造器中,因为静态属性在类加载时就需进行初始化
3.final类不能被继承,但是可以实例化对象
4.如果类不是final类,但是含有final方法,则该方法虽然不能重写,但是可以被继承
5.final类没必要再将方法修饰成final方法
6.final不能修饰构造器
7.final 和 static 往往搭配使用,效率更高,不会导致类加载。底层编译器做了优化处理。
	如调用接口中的属性,IA.n1
8.包装类(Integer,Double,Float等都是final),String也是final类

抽象类

抽象类介绍

抽象类介绍

抽象类细节

1.抽象类不能被实例化
2.抽象类不一定要包含abstract方法
  一旦类包含了abstract方法,则类必须声明为abstract
3.abstract只能修饰类和方法,不能修饰属性和其他的
4.抽象类可以有任意成员(抽象类本质还是类)
5.如果一个类继承了抽象类,则它必须实现抽象类的所有抽象方法,除非它自己也声明为abstract类
6.抽象方法不能使用private、final和static来修饰,因为这些关键字都是和重写相违背的
	静态方法只能被继承,不能被重写,子类中与父类相同的静态方法只会隐藏父类的静态方法不可见而已,二者没有关系,他们的行为不具有多态性

抽象类练习

抽象类练习

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

抽象模板设计模式

Template类
package abstract_interface.abstract_;

public abstract class Template {//抽象类-模板设计模式:子类只需重写job方法即可调用calculateTime方法呈现不同的结果
    public abstract void job();
    public void calculateTime(){
        //统计当前距离1970-1-1 0:0:0 的时间差,单位ms
        long start = System.currentTimeMillis();
        job();
        long end = System.currentTimeMillis();
        System.out.println("Job耗时: "+ (end - start)+"ms");
    }
}
Test类
package abstract_interface.abstract_;

public class Test {
    public static void main(String[] args) {
        Chicken goji = new Chicken("公鸡", 10.5);
        System.out.println(goji.howToEat());
        Job1 job1 = new Job1();
        job1.calculateTime();
    }
}

接口

接口基本介绍

介绍:
	接口就是给出一些没有实现的方法,封装到一起,到某个类要使用的时候,根据具体情况把这些方法写出来
语法:
	interface 接口名{
		//属性
		//方法(1.抽象方法 2.默认实现方法(default声明) 3.静态方法)
	}
	class 类名 implements 接口{
		自己属性;
		自己方法;
		必须实现的接口的抽象方法;
	}
小结:
	1.在jdk7.0之前 接口里的所有方法都没有方法体,即都是抽象方法
	2.jdk8.0后接口可以有静态方法,默认方法,也就是说接口中可以有方法的具体实现

接口应用场景

接口DBinterface
package abstract_interface.interface_.apply;

public interface DBinterface {
    public void connect();
    public void close();
}
MysqlDB类
package abstract_interface.interface_.apply;

public class MysqlDB implements DBinterface{
    @Override
    public void connect() {
        System.out.println("连接Mysql...");
    }

    @Override
    public void close() {
        System.out.println("关闭Mysql...");
    }
}
OracleDB类
package abstract_interface.interface_.apply;

public class OracleDB implements DBinterface{
    public void connect() {
        System.out.println("连接Oracle...");
    }

    @Override
    public void close() {
        System.out.println("关闭Oracle...");
    }
}
interface测试类
package abstract_interface.interface_.apply;

/**
 * 体会接口编程
 * 1.项目经理写接口,程序员实现接口
 * 2.接口回调,统一规范
 */
public class Interface {
    public static void main(String[] args) {
        //连接MysqlDB
        MysqlDB mysqlDB = new MysqlDB();
        test(mysqlDB);
        //连接OracleDB
        OracleDB oracleDB = new OracleDB();
        test(oracleDB);
    }
    public static void test(DBinterface dbinterface){
        //接口回调
        dbinterface.connect();
        dbinterface.close();
    }
}

接口细节

1.接口不能被实例化
2.接口中所有的方法默认为public,抽象方法可以不用abstract修饰
	如:void aaa(){} 等价于 public abstract void aaa(){}
3.一个普通类实现接口,就必须将该接口的所有抽象方法实现,
  抽象类实现接口,可以不用实现接口的方法。
4.一个类同时可以实现多个接口
5.接口中的属性,只能是final的,而且是public static final 修饰符。
	如:int a = 1; 等价于 public static final int a = 1;(必须初始化)
6.接口中属性的访问形式:接口名.属性名
7.接口不能继承其他的类,但是可以继承多个别的接口
8.接口的修饰符只能是public 和 默认,和类的修饰符一样

接口小练习

接口练习

接口VS继承

1.解决问题不同:
	继承解决代码的复用性和可维护性
	接口设计好各种规范,让其它类去实现这些方法
2.接口比继承更加灵活:
	继承满足 is-a 的关系
	而接口只需满足 like-a 的关系
3.接口在一定程度上实现代码解耦(利用接口规范 + 动态绑定机制)

接口多态特性

1.多态参数+多态数组
package abstract_interface.interface_.poly.poly_1_2;

//体会接口回调、接口多态
public class Test {
    public static void main(String[] args) {
        //多态1:多态数组

        //测试1: 使用Edible类型的数组测试
        //接口类型 引用名 = 实现该接口的类的对象的引用
        Edible[] e = new Edible[2];
        e[0] = new Chicken("鸡哥", 10.5);
        e[1] = new Apple("红色", 10.6);
        for (int i = 0; i < 2; i++) {
            System.out.println(e[i].howToEat());
        }
        //多态2:多态参数

        //测试2: 定义showInfo函数,形参是Edible类型的引用,调用函数传入不同的实参实现多态
        showMenu(new Chicken("jige",9.5));
        showMenu(new Apple("绿色",9.6));

        //测试3: 定义LittleBaby中的UnderstandObject函数,形参是Object的引用,
        //       如果实参实现Edible,则显示吃法,否则提示用户不可吃
        LittleBaby littleBaby = new LittleBaby();
        littleBaby.understandObject(new Tiger("泰格",15));
        littleBaby.understandObject(new Apple("黄色",11.6));
        littleBaby.understandObject(new Chicken("母鸡",11.6));

    }
    public static void showMenu(Edible edible){
        System.out.println(edible.howToEat());
    }

}
2.多态传递
package abstract_interface.interface_.poly.poly_3;

/**
 * 接口的多态传递现象
 * 1.接口继承产生
 * 2.类的继承产生
 */
public class PolyPass {
    public static void main(String[] args) {
        IB People1 = new People();
        IA People2 = new People();//接口继承产生多态传递现象
        IB teacher1 = new Teacher();//类继承产生多态传递现象
        IA teacher2 = new Teacher();//接口继承和类继承同时发挥作用
    }
}
interface IA{

}
interface IB extends IA{

}
class People implements IB{

}
class Teacher extends People{

}

接口练习

接口小练习

接口练习1

内部类

内部类分类

根据定义位置分:
	定义在外部类局部位置上(比如方法内、代码块内):
		1):局部内部类(有类名
		2):匿名内部类(没有类名)
	定义在外部类的成员位置上:
		1):成员内部类(没有static修饰)
		2):静态内部类(使用static修饰)

局部内部类

1.可以直接访问外部类的所有成员,包含私有的
2.不能添加访问修饰符,因为地位相当于局部变量。
  但可以用final修饰,表示不能被其他局部内部类继承
3.作用域:仅仅在定义它的方法或代码块中
4.局部内部类--->访问--->外部类的成员:直接访问
5.外部类--->访问--->局部内部类的成员:创建对象,再访问(注意:必须在作用域内)
6.外部其他类--->不能访问--->局部内部类(因为局部内部类相当于一个局部变量)
7.如果外部类和局部内部类的成员重名时,默认遵循就近原则,如果想访问外部类的成员,则可以使用(外部类.this.成员)访问
	注:外部类.this相当于外部类的一个实例对象,哪个对象调用就是哪个对象

匿名内部类

1.匿名内部类既是一个类的定义,也是一个对象
2.可以直接访问外部类的所有成员,包含私有的
3.不能添加访问修饰符,因为相当于一个局部变量
4.作用域:仅仅在定义它的方法或代码块中
5.匿名内部类--->访问--->外部类成员:直接访问
6.外部其他类--->不能访问--->匿名内部类(因为匿名内部类相当于一个局部变量)
7.如果外部类和匿名内部类的成员重名时,匿名内部类访问默认遵循就近原则,如果想访问外部类的成员,可以使用(外部类名.this.成员)访问
注:2、3、4、5、6、7均与局部内部类相同,因为位置相同
	与局部内部类不同的是:匿名内部类只定义一次,且只创建一次对象,因此不涉及外部类访问匿名内部类的问题
基于接口的匿名内部类
AnnoymousInnerClass
package innerclass.AnonymousInnerClass.interface_innerclass;

/**
 * 基于接口的匿名内部类
 */
public class AnnoymousInnerClass {
    public static void main(String[] args) {
        Outer01 outer01 = new Outer01();
        outer01.method();
    }
}

IA
package innerclass.AnonymousInnerClass.interface_innerclass;

public interface IA {
    public void say();
}

Outer01
package innerclass.AnonymousInnerClass.interface_innerclass;

/**
 * 1.father1的编译类型:IA
 * 2.father1的运行类型:匿名内部类
 * 我们看底层:
 *          class Outer01$1 implements IA{
 *              @Override
 *             public void say() {
 *                 System.out.println("我是你的爸爸1");
 *             }
 *          }
 * 3.jdk底层在创建匿名内部类 Outer01$1时,立即马上就创建了 Outer01$1实例,并且把地址返回给father1
 * 4.匿名内部类使用一次就不能再使用
 */
public class Outer01 {
    private int n1 = 10;//属性
    public void method(){//方法
        //基于接口的匿名内部类
        IA father1 = new IA() {
            @Override
            public void say() {
                System.out.println("我是你的爸爸1");
            }
        };
        //基于接口的匿名对象调用say方法
        father1.say();
        System.out.println("father1的运行类型: "+father1.getClass());//Outer01$1
        //第二种调用方式,因为其本身就是匿名对象
        new IA() {
            @Override
            public void say() {
                System.out.println("我是你的爸爸1");
            }
        }.say();
    }
}

基于类的匿名内部类
AnnoymousInnerClass
package innerclass.AnonymousInnerClass.class_innerclass;

/**
 * 基于类的匿名内部类
 * 基于抽象类的匿名内部类:匿名内部类必须实现抽象类中的抽象方法
 */
public class AnnoymousInnerClass {
    public static void main(String[] args) {
        Outer02 outer02 = new Outer02();
        outer02.method();
    }
}

Outer02
package innerclass.AnonymousInnerClass.class_innerclass;

/**
 * 1.father1的编译类型:Father
 * 2.father1的运行类型:匿名内部类
 * 3.同理底层会创建匿名内部类,同时创建匿名内部类实例对象,并将地址返回给father1
 * 4.匿名内部类使用一次就不能再使用
 * 5.参数列表 ("Jack") 会传到父类构造器
 */
public class Outer02 {
    private int n1 = 10;//属性
    public void method(){//方法
        Father father1 = new Father("Jack"){//此处Jack会传到父类构造器并进行初始化
            @Override
            public void test() {
                System.out.println("父类test方法被重写...");
            }
        };
        System.out.println("father1的运行类型: "+father1.getClass());//Outer02$1
        father1.test();
        Animal animal = new Animal() {
            @Override
            public void eat() {
                System.out.println("吃饭");
            }
        };
        animal.eat();
    }
}

Father
package innerclass.AnonymousInnerClass.class_innerclass;

public class Father {
    private String name;

    public Father(String name) {
        this.name = name;
    }

    public void test(){
        System.out.println("父类test方法...");
    }
}

Animal
package innerclass.AnonymousInnerClass.class_innerclass;

public abstract class Animal {
    public abstract void eat();
}

匿名内部类的应用
当作实参直接传递

匿名内部类应用1

匿名内部类课堂练习

匿名内部类应用2.2

package innerclass.AnonymousInnerClass.example;

/**
 * 1.有一个铃声接口Bell,里面有一个ring方法
 * 2.有一个手机类CellPhonex,具有闹钟功能alarmclock,参数是Bell类型
 * 3.测试手机类的闹铃功能,通过匿名内部类(对象)作为参数,打印:懒猪起床了
 * 4.在传入另一个匿名内部类(对象),打印:小伙伴上课了
 * 5.实质:Bell是接口,等下匿名内部类基于该接口创建
 *        CellPhone是外部类,alarmclock是方法,方法的实参采用匿名内部类
 *        Example主方法中创建外部类对象,调用alarmclock方法,实参传入不同的匿名内部类,呈现不同的结果
 */
public class Example{
    public static void main(String[] args) {
        CellPhone cellPhone = new CellPhone();
        cellPhone.alarmclock(new Bell() {
            @Override
            public void ring() {
                System.out.println("懒猪起床了...");
            }
        });
        cellPhone.alarmclock(new Bell() {
            @Override
            public void ring() {
                System.out.println("小伙伴上课了...");
            }
        });
    }
}
class CellPhone {
    public void alarmclock(Bell bell){
        bell.ring();
    }

}
interface Bell{
    void ring();
}

成员内部类

1.可以直接访问外部类的所有成员,包含私有的
2.可以添加任意访问修饰符(public、protected、默认、private),因为它地位就是一个成员
3.作用域:整个外部类类体
4.成员内部类--->访问--->外部类:直接访问
5.外部类--->访问--->成员内部类:创建对象再访问
6.外部其他类--->访问--->成员内部类:
	三种方式:
		(1):Outer01 outer01 = new Outer01();
			Outer01.Inner01 inner01 = outer01.new Inner01();
		(2):Outer01.Inner01 inner01 = new Outer01().new Inner01();
		(3)://使用一个方法来获取,更加简洁
			Inner01 inner01Instance = new Outer01().getInner01Instance();
7.如果外部类和内部类的成员重名时,内部类访问的话,默认遵循就近原则,如果访问外部类的成员,采用(外部类名.this.成员)

静态内部类

放在外部类的成员位置,使用static修饰
1.可以直接访问外部类的所有静态成员,包含私有的,但不能直接访问非静态成员
2.可以添加任意访问修饰符,因为地位就是一个静态成员
3.作用域:外部类整个类体
4.静态内部类--->访问--->外部类(比如:静态属性):直接访问
5.外部类--->访问--->静态内部类:创建对象再访问
6.外部其他类--->访问--->静态内部类:
	两种方式:
		(1)://因为静态内部类,可通过类名直接访问
			Outer01.Inner01 inner01 = new Outer01.Inner01();
		(2)://编写一个方法,返回静态内部类的对象实例
			//普通方法,通过外部类对象调用
			Outer01.Inner01 inner01 = outer01.getInner01();
			//静态方法,通过外部类名调用
			Outer01.Inner01 inner01_ = Outer01.getInner01_();

再访问
6.外部其他类—>访问—>成员内部类:
三种方式:
(1):Outer01 outer01 = new Outer01();
Outer01.Inner01 inner01 = outer01.new Inner01();
(2):Outer01.Inner01 inner01 = new Outer01().new Inner01();
(3)😕/使用一个方法来获取,更加简洁
Inner01 inner01Instance = new Outer01().getInner01Instance();
7.如果外部类和内部类的成员重名时,内部类访问的话,默认遵循就近原则,如果访问外部类的成员,采用(外部类名.this.成员)


### 静态内部类

放在外部类的成员位置,使用static修饰
1.可以直接访问外部类的所有静态成员,包含私有的,但不能直接访问非静态成员
2.可以添加任意访问修饰符,因为地位就是一个静态成员
3.作用域:外部类整个类体
4.静态内部类—>访问—>外部类(比如:静态属性):直接访问
5.外部类—>访问—>静态内部类:创建对象再访问
6.外部其他类—>访问—>静态内部类:
两种方式:
(1)😕/因为静态内部类,可通过类名直接访问
Outer01.Inner01 inner01 = new Outer01.Inner01();
(2)😕/编写一个方法,返回静态内部类的对象实例
//普通方法,通过外部类对象调用
Outer01.Inner01 inner01 = outer01.getInner01();
//静态方法,通过外部类名调用
Outer01.Inner01 inner01_ = Outer01.getInner01_();


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值