Java 基础(对象)六

303 阅读21分钟

对象Object

关键字: static

static 关键字的使用

主要用来修饰类的内部结构

  • static: 静态的

  • static可以用来修饰: 属性、方法、代码块、内部类

  • static修饰属性: 静态变量(类变量)

    1. 属性: 按是否使用static修饰,分为: 静态属性 vs 非静态属性(实例变量)

      • 静态属性(变量): 当我们创建了类的多个对象,多个对象共享同一个静态变量
      • 实例变量: 每个对象都独立拥有一套类中的非静态属性
    2. static修饰属性的其它说明:

      • 静态变量随着类的加载而加载,类可以直接调用静态变量
      • 静态变量的加载要早于对象的创建
      • 由于类只会加载一次,所以静态变量在内存中只会存储一份,并存储在方法区的静态域
    3. 静态属性举例: System.out、Math.PI...

    4. 内存结构:

      • 栈: 局部变量
      • 堆: new的结构、对象、数组
      • 方法区: 类的加载信息(反射)、静态域(类变量)、常量池(String)
  • static修饰方法: 静态方法

    1. 随着类的加载而加载,类可以直接调用静态方法
    2. 静态方法中,只能调用静态属性和静态方法,并且不能使用this关键字(生命周期不同)
    3. 非静态方法中,既可以调用静态属性和方法也可以调用非静态属性和方法
  • static 注意点:

    1. 在静态方法内,不能使用this、super关键字
    2. 关于静态属性和静态方法的使用,可以从生命周期(类、对象)的角度去理解
  • 开发中如何确定一个属性、方法是否要声明为static

    1. 属性是可以被多个对象共享的,不会随着对象的不同而不同
    2. 类中的常量也经常声明为static
    3. 属性是静态的,那对应的操作静态属性的get、set方法也是静态的
    4. 工具类中的方法,习惯上声明为static的;比如: Math、Arrays、Collections(集合)...
public class StaticTest {
	public static void main(String[] args) {
    
    	Chinese.nation = "中国";
        
    	Chinese c1 = new Chinese();
        c1.name = "lv";
        c1.age = 20;
        c1.nation = "CHN";
        Chinese c2 = new Chinese();
        c2.name = "cc";
        c2.age = 30;
        c2.nation = "CHINA";
        System.out.println(c1.nation); // CHINA
        
        Chinese.eat(); // CHINA人 eat 饺子!
    }
}
class Chinese {
	String name;
    int age;
    static String nation;
    public static void eat () {
    	System.out.println(nation + "人 eat 饺子 !");
    }
}

// static 的应用
// 1.
public class CircleTest {
	public static void main(String[] args) {
    	Circle c1 = new Circle();
        Circle c2 = new Circle();
        System.out.println("c1: " + c1.getId() + ", c2: " + c2.getId()); // c1: 1001, c2: 1002
        System.out.println("circle num: " + Circle.getTotal()); // 2
    }
}
class Circle {
	private double radius;
    private int id;
    private static int total;
    private static int init = 1001;
    public Circle () {
    	id = init++; // id自动添加
        total++; // total自动添加
    }
    public Circle (double radius) {
    	this();
        this.radius = radius
    }
    public int getId () {
    	return id;
    }
    public static int getTotal () {
    	return total;
    }
    public double findArea () {
    	return 3.14 * radius * radius;
    }
}
// 2.
public class AccountTest {
	public static void main(String[] args) {
		Account a1 = new Account();
		Account a2 = new Account("123qwe", 123);

		Account.setInterestRate(0.012);
		Account.setMinMoney(1.0);

		System.out.println(a1);
		System.out.println(a2);
		System.out.println("interestRate: " + Account.getInterestRate());
		System.out.println("minMoney: " + a1.getMinMoney());
	}
}
public class Account {

	private int id;
	private String pwd = "000000";
	private double balance;
	private static double interestRate;
	private static double minMoney = 1.0;
	private static int init = 1001;

	public Account () {
		id = init++;
	}
	public Account (String pwd, double balance) {
		this();
		this.pwd = pwd;
		this.balance = balance;
	}
	public int getId () {
		return id;
	}
	public String getPwd () {
		return pwd;
	}
	public void setPwd (String pwd) {
		this.pwd = pwd;
	}
	public double getBalance () {
		return balance;
	}
	public static double getInterestRate () {
		return interestRate;
	}
	public static void setInterestRate (double interestRate) {
		Account.interestRate = interestRate;
	}
	public static double getMinMoney () {
		return minMoney;
	}
	public static void setMinMoney (double minMoney) {
		// static中不能出现非静态属性和方法、this、super...
        // 使用类名调用静态属性和方法
        Account.minMoney = minMoney;
	}
	public String toString () {
		return "Account [ id = " + id + " , pwd = " + pwd + " , balance = " + balance + " ]";
	}
}

单例(Singleton)设计模式(static的应用)

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

单例设计模式: 就是采用一定的方法保证在整个软件系统中,对某个类只能存在一个对象实例,并且该对象只能提供一个取得其对象实例的方法;如果让某个类在一个虚拟机中只产生一个对象,我们首先必须将类的构造器的访问权限设置为private,这样,就不能用new操作符在类外部生成类的对象,但在类内部仍能产生该类的对象;因为在类的外部开始还无法得到类的对象,只能调用该类的某个静态方法以返回类内部创建的对象,静态方法只能访问类中的静态成员变量,所以,指向类内部产生的该类对象的变化也必须定义为静态的

单例设计模式分类:

  • 饿汉式: 初始化的时候就创建好
public class SingletonTest1 {
	public static void main(String[] args) {
    
    	Bank b1 = Bank.getBankInstance();
        Bank b2 = Bank.getBankInstance();
        System.out.println("true ? " + (b1 == b2)); // true
    }
}
// 饿汉式1
public class Bank {
	// 1. 私有化构造器
	private Bank () {}
    // 2. 内部创建类的对象,必须声明为静态的
    private static Bank instance = new Bank();
    // 3. 提供公共的静态方法,返回类的对象
    public static Bank getBankInstance () {
    	return instance;
    }
}
// 饿汉式2
public class Bank1 {
	// 1. 私有化构造器
	private Bank () {}
    // 2. 内部创建类的对象,必须声明为静态的
    private static Bank1 instance = null;
    static {
    	instance = new Bank1();
    }
    // 3. 提供公共的静态方法,返回类的对象
    public static Bank1 getBankInstance () {
    	return instance;
    }
}

  • 懒汉式: 啥时候用啥时候才创建
public class SingletonTest2 {
	public static void main(String[] args) {
    	Order o1 = Order.getOrderInstance();
        Order o2 = Order.getOrderInstance();
        System.out.println("true ? " + (o1 == o2)); // true
    }
}
public class Order {
	// 1. 私有化类的构造器
	private Order () {}
    // 2. 声名当前类对象,无初始化
    private static Order instance = null;
    // 3. 提供公共的方法,返回内部对象
    public static Order getOrderInstance () {
    	if (instance == null) {
        	instance = new Order();
        }
    	return instance;
    }
}

饿汉式和懒汉式的区分:

  • 饿汉式

    • 优点: 天然就是线程安全的
    • 缺点: 初始化创建对象,长期占用内存空间
  • 懒汉式

    • 优点: 延迟对象的创建,节省内存空间
    • 缺点: 目前的写法线程不是安全的(多线程内容时修改)

单例模式的优点:

由于单例模式只生成一个实例,减少了系统性能开销,当一个对象的产生需要比较多的资源时,如读取配置产生其它依赖对象时,则可以通过在应用启动时直接产生一个单例对象,然后永久驻留内存的方式来解决。

  • java.lang.Runtime 就是典型的单例模式
public class Runtime {
	private static Runtime currentRuntime = new Runtime();
    public static Runtime getRuntime () {
    	return currentRuntime;
    }
    private Runtime () {}
    
    // ...
}
  • 应用场景:

    1. 网站计数器
    2. 应用程序的日志应用
    3. 数据库连接池
    4. 项目中,读取配置文件的类
    5. Application 单例的典型应用
    6. Windows的Task Manager(任务管理器)
    7. Windows的Recycle Bin(回收站)

理解main方法的语法

  • main()方法作为程序的入口出现
  • main()方法也是一个普通的静态方法(static)
  • main()方法中的形参,也可以作为我们与控制台交互的方式(之前,使用Scanner),控制台中输入: java MainDemo "87" "96" "99"
public class MainDemo {
	public static void main(String[] args) {
    	for (int = i; i < args.length; ++i) {
        	System.out.println("***: " + args[i]);
            int num = Integer.parseInt(args[i]);
            System.out.println("###: " + num);
        }
    }
}

类的成员之四: 代码块(初始化块)

代码块类似于方法体

  • 核心: 清楚代码块何时执行

  • 代码块的作用: 初始化类、对象的信息

  • 代码块的修饰: 只能使用static

  • 分类: 静态代码块 vs 非静态代码块

  • 静态代码块

    1. 内部可以有输出语句
    2. 随着类的加载而执行(自动执行,且只执行一次)
    3. 作用: 初始化类的属性
    4. 类中存在多个静态代码块时,按照代码的执行顺序执行
    5. 静态代码块肯定优先于非静态代码块执行
    6. 代码块中只能调用静态结构
  • 非静态代码块

    1. 内部可以有输出语句
    2. 随着对象的创建而执行(且new一次执行一次)
    3. 作用: 创建对象时,对属性初始化
    4. 类中存在多个非静态代码块时,按照代码的执行顺序执行
    5. 可以调用静态结构和非静态结构
    6. 应用场景: 创建数据库连接池、...
  • 常见的对象属性赋值:

    1. 默认初始化
    2. 显示初始化
    3. 构造器初始化
    4. 对象.属性赋值
    5. 代码块赋值

    执行的先后顺序: 1 - 2 / 5(由程序书写位置决定) - 3 - 4

  • 实例化子类对象时,涉及到父类、子类中静态代码块、非静态代码块、构造器的加载顺序

    • static: 由父及子,静态先行

    • 代码块先于构造器执行

    1. 先执行 父类的静态代码块、子类的静态代码块
    2. 后执行 父类的非静态代码块、父类的构造器
    3. 在执行 子类的非静态代码块、子类的构造器
class BlockTest {
	public static void main(String[] args) {
    	String desc = Person.desc;
        System.out.println(desc);
        Person p1 = new Person();
        Person p2 = new Person();
        System.out.println(p1.age);
        // Hello, static block!-1
        // Hello, static block!-2
        // 我是一个man!
        // Hello, block!
        // Hello, block!
        // 10
    }
}
class Person {
	String name;
    int age;
    static String desc = "一个person";
    public Person () {}
    public Person (String name, int age) {
    	this.name = name;
        this.age = age;
    }
    
    // 代码块
    // static代码块
    static{
    	System.out.println("Hello, static block!-1");
        desc = "我是一个man!";
    }
    static{
    	System.out.println("Hello, static block!-2");
        desc = "我是一个man!";
    }
    // 非static代码块
    {
    	System.out.println("Hello, block!");
        age = 10;
    }
    public void eat () {
    	System.out.println("eat");
    }
}

关键字: final

概念

  • final: 最终的

  • final可以修饰的结构: 类、方法、变量

作用:

  • final修饰类: public final class A {},不能有子类‘太监类’,如: String类、System类、StringBuffer类...

  • final修饰方法: public final void getA() {},此方法不可以被重写,如: Object.getClass()

  • final修饰变量: 此时的‘变量’就称为常量

    1. final修饰属性: 可以赋值的位置有,显示初始化(值相同)、代码块中初始化(需要逻辑处理)、一个类的每个构造器中初始化(每个对象的值不同)
    2. final修饰局部变量:
  • static final: 两者结合,可以修饰属性、方法

    1. 修饰属性: 称为全局常量(接口中常见)
    2. 修饰方法: 个人使用较少
final class FinalA {
	final int num = 10;
    final int LEFT;
    final int RIGHT;
    
    {
    	LEFT = 1;
    }
    public FinalA() {
    	RIGHT = 10;
    }
    public FinalA(int n) {
    	RIGHT = n;
    }
    public void setNum () {
    	// num = 20; // error
    }
    public void show() {
    	final int NUM = 10;
        // NUM += 1; error
    }
    public void show1(final int num) {
    	// num 只能使用不能修改
        System.out.println(num);
    }
}

class AA {
	public final void getA () {}
}
class BB extends AA {
	// public void getA () {} // error: cannot overwrite
}

// test
public class Something {
	public static void main(String[] args) {
    	Other o = new Other();
        new Something().addOne(o); // success
    }
    public void addOne(final Other o) {
    	// o = new Other(); error
    	o.i++; // success
    }
}
class Other {
	public int i;
}

抽象类与抽象方法(abstract)

抽象类 就是比普通类多定义了抽象方法,除了不能直接进行类的实例化操作外,并没有任何不同

概念

  • abstract: 抽象的
  • abstract可以用来修饰的结构: 类、方法

作用

abstract修饰类 - 抽象类

  • 此类不能实例化
  • 但类中一定有构造器,便于子类实例化调用
  • 开发中,都会提供抽象类的子类,让子类对象实例化,完成相关操作

abstract修饰方法: 抽象方法

  • 抽象方法只有方法的声明,没有方法体
  • 包含抽象方法的类,一定是一个抽象类;反之不成立
  • 若子类重写了(直接、间接)父类中的所有抽象方法后,此子类方法实例化
  • 若子类没有重写(直接、间接)父类中的所有抽象方法,此子类只能是一个抽象类
abstract class Person {
	String name;
    int age;
    public Person () {}
    public Person (String name, int age) {
    	this.name = name;
        this.age = age;
    }
    // 不是抽象方法
    // public void eat () {
    	// System.out.println("person eat!");
    // }
    // 抽象方法
    public abstract void eat();
    
    public void walk () {
    	System.out.println("person walk!");
    }
}
class Student extends Person {
	public Student (String name, int age) {
    	super(name, age);
    }
    // 必须重写父类中的抽象方法
    public void eat () {
    	System.out.println("student eat!");
    }
}

应用

Java允许类设计者指定: 超类声明一个方法但不提供实现,该方法的实现由子类提供;这样的方法称为抽象方法。有一个或更多抽象方法的类称为抽象类

注意点

  • abstract 只能修饰: 类、方法两种结构
  • abstract 不能修饰: 私有方法、静态方法、final的方法、final类
public class EmployeeTest {
	public static void main(String[] args) {
    	// 多态 - 没有多态,抽象类就没有意义
    	Employee m1 = new Manager('kk', '1001', 500, 5000);
        m1.work();
    }
}
public abstract class Employee {
	private String name;
    private String id;
    private double salary;
    public Employee () {}
    public Employee (String name, String id, double salary) {
    	super();
        this.name = name;
        this.id = id;
        this.salary = salary;
    }
    public abstract void work();
}
public class Manager extends Employee {
	private double bonus;
    public Manager (double bonus) {
    	super();
        this.bonus = bonus;
    }
    public Manager (String name, String id, double salary, double bonus) {
    	super(name, id, salary);
        this.bonus = bonus;
    }
    public void work () {
    	System.out.println("manage employeer");
    }
}

抽象类的匿名子类

目的: 简单、省事,不用编写抽象类的子类代码

  • 匿名子类: new AbstractFatherClass(){ // code };
public class PersonTest {
	public static void main(String[] args) {
    	// 非匿名类匿名对象
    	methd1(new Student());
        
        // 1.抽象类的匿名子类的对象
        Person p1 = new Person() { // 多态
        	// override
            public void walk() {
            	System.out.println("walk!");
            }
        };
        method(p1);
        // 2.抽象类的匿名子类的对象
        method(new Person(){
        	public void walk() {
            	System.out.println("walk2!");
            }
        });
    }
    public static void method (Person p1) {
    	p1.walk();
    }
    public static void method1 (Student s1) {
    	s1.walk();
    }
}

抽象类的应用: 模板方法设计模式(TemplateMethod)

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

解决的问题:

  • 当功能内部一部分实现是确定的,一部分实现是不确定的,这是可以将不确定的部分暴露出去,让子类去实现
  • 换句话说,在软件开发中实现一个算法时,整体步骤很固定、通用,这些步骤已在父类中写好;但某些部分易变,易变部分可以抽象出来,供不同子类实现;这就是一种模板模式。
public class TemplateTest {
	public static void main(String[] args) {
    	SubTemplate t = new SubTemplate();
        t.spendTime();
    }
}
abstract class Template {
	// 计算某段代码需要花费的时间,final-不许重写
	public final void spendTime () {
    	// 当前时间
    	long start = System.currentTimeMillis();
        
        // 易变部分
        // this.code(); 当前对象重写的方法
        code();
        // ...
        
        // 结束时间
        long end = System.currentTimeMillis();
        System.out.println("spend time: " + (end - start));
    }
    public abstract void code();
}
class SubTemplate extends Template {
	public void code () {
    	// 1000 以内的质数
    	for (int = 2; i <= 1000; i++) {
        	boolean isFlag = true;
            for (int j = 2; j <= Math.sqrt(i); j++) {
            	if (i % j == 0) {
                	isFlag = false;
                    break;
                }
            }
            if (isFlag) {
            	System.out.println(i);
            }
        }
    }
}

接口(interface)

概述:

  • 一方面,有时必须从几个类中派生出一个子类,继承它们所有的属性和方法;但是,Java不支持多重继承;有了接口(与类是并列关系),就可以实现多重继承的效果。
  • 另一方面,有时必须从几个类中抽取出一些共同的行为特征,而它们之间又没有is-a的关系,仅仅具有相同的行为特征而已;如:鼠标、键盘、打印机等都支持USB连接。
  • 接口就是规范,定义的是一组规则,体现了显示世界中 "如果你是/要...则必须能..."的思想;继承是一个"是不是"的关系,而接口实现则是"能不能"的关系
  • 接口的本质是契约,标准、规范,就像法律一样,制订后每个人都要遵守。

使用:

概念

  • 接口使用interface定义

  • Java中 接口和类 是并列结构

  • Java开发中,接口通过让类去实现(implements)的方式来使用

    1. 如果实现类覆盖了接口中的所有抽象方法,则此实现类就可以实例化
    2. 如果实现类没有覆盖接口中的所有抽象方法,则此实现类仍为一个抽象类
  • 接口和接口之间可以继承(extends),而且可以多继承

  • 接口的具体使用,体现多态性

  • 接口,实际上可以看做是一种规范

  • 抽象类与接口的异同:

    1. 都不能实例化
    2. 都可以写抽象方法
    3. 抽象类可以被继承,接口可以被实现

定义接口中的成员

  • JDK7及以前: 只能定义全局常量和抽象方法

    1. 全局常量(可以省略): public static final type valueName: value;
    2. 抽象方法: public abstract xxx methodName();
  • JDK8: 除了定义全局常量和抽象方法外,还可以定义静态方法、默认方法

    1. 静态方法: public static type className () { //... };接口中定义的静态方法,只能通过接口调用
    2. 默认方法: 通过实现类的对象,可以调用接口中的默认方法,默认方法在实现类中可以重写
  • Java类可以实现多个接口(接口之间通过 ‘,’ 逗号分隔) -> 弥补了Java单继承性的局限性

  • 一个类可以同时继承父类和实现多个接口 class AA extends BB implements I1,I2,...

注意事项

  • 接口中不能定义构造器,意味着接口不能实例化
// JDK7
public class InterfaceTest {
	public static void main(String[] args) {
    	System.out.println(Flyable.MAX_SPEED);
        System.out.println(Flyable.MIN_SPEED);
        // Flyable.MIN_SPEED = 1; error
        Plane p1 = new Plane();
        p1.fly(); // "fly!"
    }
}

interface Flyable {
	// 全局常量
    public static final int MAX_SPEED = 7900; // 第一宇宙速度
    // public static final int MIN_SPEED = 1;
    int MIN_SPEED = 1; // 可以省略,默认添加
    
    // 抽象方法
    public abstract void fly();
    void stop(); // 可省略,默认添加
}
class Plane implements Flyable {
	// Override-实现
	public void fly() {
    	System.out.println("fly!");
    }
    // Override-实现
    public void stop() {
    	System.out.println("stop!");
    }
}
abstract class Kite implements Flyable {
	public void fly() {
    	System.out.println("fly!");
    }
}
interface Attackable {
	// public abstract
	void attack();
}
class Bullet implements Flyable,Attackable,CC {
	public void attack() {
    	System.out.println("attack!");
    }
    public void fly() {
    	System.out.println("fly!");
    }
    public void stop() {
    	System.out.println("stop!");
    }
    public void method1() {}
    public void method2() {}
}
interface AA {
	void method1();
}
interface BB {
	void method2();
}
interface CC extends AA,BB{
	// ...
}

// Java8
public interface CompareA {
	// 静态方法 - static
	public static void method1 () {
    	System.out.println("接口: 静态方法");
    }
    // 默认方法 - default
    public default void method2 () {
    	System.out.println("接口: 默认方法");
    }
    // 省略 public
    default void method3 () {
    	System.out.println("接口: 可以省略public");
    }
}
public interface CompareB {
	public default void method3 () {
    	System.out.ptingln("compareB method3");
    }
}
public class SubClassTest {
	public static void main (String[] args) {
    	SubClass s1 = new SubClass();
        // s1.method1(); error
        CompareA.method1(); // 静态方法只能通过接口调用
        s1.method2();
        // 如果子类(实行类)继承的父类和实现类的接口中声明了
        // 同名同参数的默认方法,那么子类在没有重写此方法的情况下,
        // 默认调用的是父类中同名同参数的方法 -> 类(方法)优先原则
        // 如果实现类实现了多个接口,而这个接口中定义了
        // 同名同参数的默认方法,那么在实现类没有重写此方法的
        // 情况下,会报错 -> 接口冲突
        // 这就需要我们在实现类中重写此方法
        s1.method3();
    }
}
class SuperClass {
	public void method3 () {
    	System.out.println("superclass method3");
    }
} 
class SubClass implements CompareA, CompareB {
	public void method2 () {
    	System.out.println("实现类重写接口默认方法");
    }
    public void method3 () {
    	System.out.println("subClass 重写method3方法");
    }
    // 如何在子类、实现类中调用父类、接口中被重写的方法
    public void myMethod () {
    	method3(); // 直接重写的方法
        super.method3(); // 父类的方法
        CompareA.super.method3(); //接口中的方法
    }
}
interface Filial {
	default void help () {
    	System.out.println("son help me~!");
    }
}
interface Spoony {
	default void help () {
    	System.out.println("man help me!");
    }
}
class Fath {
	public void help () {
    	System.out.println("sn help mother");
    }
}
class Man extends Fath implements Filial, Spoony {
	public void help () {
    	Filial.super.help();
        Spoony.super.help();
    }
}

接口的使用(实现类)

  • 接口使用也满足多态性
  • 接口,实际上就是定义了一种规范
  • 开发中,体会面向接口编程
  • 匿名实现类
// USB类似于一个规范
public class USBTest {
	public static void main(String[] args) {
		// 1. 创建了接口的非匿名实现类的非匿名对象
        Computer c1 = new Computer();
		Flash f1 = new Flash();
		c1.transferData(f1);
        // 2. 创建了接口的非匿名实现类的匿名对象
        com.transferData(new Printer());
        // 3. 创建了接口的匿名实现类的非匿名对象
        USB phone = new USB() {
        	public void start() {
            	//
            }
            public void stop() {
            	//
            }
        };
        c1.transferData(phone);
        // 4. 创建了接口的匿名实现类的匿名对象
        c1.transferData(new USB() {
        	public void start() {
            	//
            }
            pubic void stop() {
            	// 
            }
        });
	}
}
class Computer {
	// USB u = new Flash();多态性
	public void transferData(USB u) {
		u.start();
		System.out.println("data.......");
		u.stop();
	}
}
interface USB {
	void start();
	void stop();
}
// 接口的实现类
class Flash implements USB {
	public void start() {
		System.out.println("Flash start work!");
	}
	public void stop() {
		System.out.println("Flash stop work!");
	}
}
class Printer implements USB {
	public void start() {
		System.out.println("Printer start work!");
	}
	public void stop() {
		System.out.println("Printer stop work!");
	}
}

接口的应用: 代理模式(Proxy)

概述:

代理设计就是为其它对象提供一种代理以控制对这个对象的访问。

简单理解就是,在使用某个对象的方法前,通过代理去判断一下当前当前的环境,再由代理去调用此对象的方法

实例:

public class NetWorkTest {
	public static void main(String[] args) {
    	Server s1 = new Server();
        ProxyServer p1 = new ProxyServer(s1);
        p1.browse();
    }
}
interface NetWork {
	public void browse();
}
// 被代理类
class Server implements NetWork {
	public void browse() {
    	System.out.println("native server!");
    }
}
// 代理类
class ProxyServer implements NetWork {
	private NetWork work;
    public ProxyServer(NetWork work) { // 多态性
    	this.work = work;
    }
	public void check() {
    	System.out.println("proxyserver check net!");
    }
    public void browse() {
    	check();
        work.browse();
    }
}
public class ProxyTest {
	public static void main(String[] args) {
		Server s1 = new Server();
		ProxyServer p1 = new ProxyServer(s1);
		p1.browse();
	}
}
interface NetWork {
	public void browse();
}

class Server implements NetWork {
	public void browse() {
		System.out.println("native server!");
	}
}
class ProxyServer implements NetWork {
	private NetWork work;
	public ProxyServer(NetWork work) {
		this.work = work;
	}
	public void check() {
		System.out.println("check net!");
	}
	public void browse() {
		check(); // 执行前处理部分
		work.browse();
	}
}

应用场景:

  • 安全代理: 屏蔽对真实角色的直接访问
  • 远程代理: 通过代理类处理远程方法调用(RMI)
  • 延迟加载: 现加载轻量级的代理对象,真正需要再加载真实对象;比如在查看大文件时,不可能将文件中所有图片都显示,只有在需要查看某个图片时,才使用proxy进行大图片的打开

分类:

  • 静态代理(静态定义代理类)
  • 动态代理(动态生成代理类)(JDK自带的动态代理,需要反射等知识)

接口的应用: 工厂模式(造对象)

概念:

工厂模式: 实现了 创造者(new)与 调用者(p1.xxx) 的分离,即将创建对象的具体过程屏蔽隔离起来,达到提高灵活性的目的

其实设计模式和面向对象设计原则都是为了使得开发项目更加容易扩展和维护,解决方式就是一个分工

工厂模式分类:

  • 简单工厂模式:

  • 工厂方法模式:

  • 抽象工厂模式:

面试题

// 1.
interface A {
	int x = 0;
    int y1 = 2;
}
class B {
	int x = 1;
    int y2 = 3;
}
class extends B implements A {
	public void pX () {
    	// System.out.println(x); // error ambigous
        System.out.println(super.x); // 1
        System.out.println(A.x); // 0 全局常量
        System.out.println(y1, y2); // 2, 3
    }
}
// 2.
interface Playable {
	void play();
}
interface Bounceable {
	void play();
}
interface Rollable extends Playable, Bounceable {
	// 全局常量 - public static final
	Ball ball = new Ball("PingPang");
}
class Ball implements Rollable {
	private String name;
    public Ball(String name) {
    	this.name = name;
    }
    public String getName () {
    	return name;
    }
    public void play () {
    	// 接口中的变量都是常量
    	// ball = new Ball("Footable"); // error 
        System.out.println(ball.getName());
    }
}

类的成员之五: 内部类

概念:

Java中允许将一个类A声明在一个类B中,则A就是内部类,B就是外部类

  • 内部类分类:

    1. 成员内部类(静态 | 非静态): 直接定义在类中
    2. 局部内部类: 定义在方法、代码块、构造器内

成员内部类

  • 一方面,作为外部类的成员:

    1. 可以调用外部类的成员
    2. 可以被static修饰
    3. 可以被四种不同的权限修饰
  • 另一方面,作为一个类:

    1. 类内可以定义: 属性、方法、构造器...
    2. 可以使用final修饰,表示此类不能被继承;不使用final,就可以被继承
    3. 可以被abstract修饰,不能实例化

局部内部类

在局部内部类的方法(show)中,如果调用局部内部类所在的方法中的局部变量(num),要求此局部变量声明为 final

  • JDK7及之前版本: 要求此局部变量声明为final
  • JDK8及之后版本: 可以省略final声明
public void method () {
	final int num = 10;
    class AA {
    	public void show () {
        	// num = 20; error
        	System.out.println(num);
        }
    }
}

注意点

  • 成员内部类和局部内部类,在编译后,都会生成字节码文件

    1. 成员内部类: 外部类$内部类名.class
    2. 局部内部类: 外部类$数字(1、2...)内部类名.class

实际应用:

public class InnerClassTest {
	
}
class Person {
	// 静态成员内部类
	static class Leg {
    	// ...
    }
    // 非静态
    class Hand {
    	// ...
    }
    class Bird {
    	String name;
        public Bird () {
        }
        { // ... }
        public void sing () {
        	System.out.println("singsong");
            Person.this.method(); // 调用外部类的方法
        }
    }
    // 局部内部类
	public Person {
    	class CC {
        	// ...
        }
    }
	public void method () {
    	// 局部内部类
        class AA {
        	// ...
        }
    }
    {
    	// 局部内部类
    	class BB {
        	// ...
        }
    }
}

内部类关注的问题

实例化成员内部类的对象

  • 静态内部类: Person.Dog d1 = new Person.Dog();
  • 非静态内部类: Person p1 = new Person(); p1.Bird b1 = p1.new Bird();

在成员内部类中区分调用外部类的结构

  • 方法形参,直接调用 xxx
  • 内部类属性,this.xxx
  • 外部类属性,Person.this.xxx

开发中局部内部类的使用

class Test {
	public static void main(String[] args) {
    	// 创建Dog实例(静态成员内部类)
        Person.Dog d1 = new Person.Dog();
        d1.show();
        // 创建Bird实例(非静态成员内部类)
        Person p1 = new Person();
        Person.Bird b1 = p.new Bird();
        b1.sing();
    }
}
class Person {
	String name;
    int age;
	static class Dog {
    	public void show () {
        	System.out.println("inner class Dog");
        }
    }
    class Bird {
    	String name;
    	public void sing () {
        	System.out.println("singsong");
            System.out.println(age); // 无重名,无冲突
        }
        public void display (String name) {
        	System.out.println(name); // 方法形参
            System.out.println(this.name); // 内部类属性
            System.out.println(Person.this.name); // 外部类属性
        }
    }
    
    // 局部内部类的使用
    public Comparable getComparable () {
    	// 创建一个实现了Comparable接口的类: 局部内部类
        // 方式一:
    	class MyComparable implements Comparable {
        	public int compareTo(Object o) {
            	return 0;
            }
        }
        return new MyComparable();
        // 方式二: 接口匿名实现类的匿名对象
        return new Comparable(Object o) {
        	public int compareTo() {
            	return 0;
            }
        }
    }
}

总结

static修饰的属性,相较于实例变量,有哪些特别之处

  • 存放在方法区的静态域中
  • 随着类的加载而加载,早于对象的创建,具有唯一性
  • 只要权限允许,可以通过 ‘对象.static’ 使用
  • static 修饰的方法中只能调用 static修饰的属性和方法

final 可以用来修饰的结构,分别表示的意思

  • 类、变量、方法

  • 类: 不能被继承

  • 变量: 不能修改初始值(显示赋值、构造器、代码块)

  • 方法: 不能被重写

单例模式:饿汉式

class Singleton1 {
    private Singleton1 () {}
    private static Singleton1 instance = new Singleton1();
    public static Singleton1 getInstance () {
    	return instance;
    }
}

单例模式: 懒汉式

class Singleton2 {
    private Singleton2 () {}
    private static Singleton2 instance = null;
    public static Singleton2 getInstance () {
    	if (instance == null) {
        	instance = new Singleton2();
        }
    	return instance;
    }
}

类的属性赋值的位置有哪些,先后顺序

  • 默认赋值
  • 显示赋值、代码块赋值(代码顺序)
  • 构造器赋值
  • 对象赋值

public static void main(String[] args) { // code }

  • 权限修饰符: private 缺省 protected public ---> 封装性
  • 修饰符: static、final、abstract、native 可以修饰方法
  • 返回值类型: void(return;)、有返回值(return value;)
  • 方法名: 标识符命名规则 - 见名知意
  • 形参列表: 重载 vs 重写(继承性),参数的值传递机制(对象的多态性)
  • 方法体: 体现方法的功能

abstract能修饰的结构,修饰后的特点

修饰的结构: 类、方法

特点

  • 不能实例化对象,提供子类进行实例化
  • 抽象方法自定义了功能的标准,具体执行需要子类去实现

接口是否能继承接口,抽象类是否能实现接口,抽象类是否能继承非抽象类

声明抽象类,并包含抽象方法,测试类中创建一个继承抽象类的匿名子类的对象

public abstract class Per {
	public abstract void method ();
}
Per p1 = new Per() {
	public void method () {
    	// ...
    }
};

抽象类和接口有哪些异同点

共同点

  • 不能实例化
  • 都能继承

不同点

  • 定义方式不同
  • 抽象类可以声明构造器
  • 接口可以被多继承,类只能单继承

如何创建静态成员内部类和非静态成员内部类

Person.Leg l1 = new Person.Leg();
Person p1 = new Person();
p1.Nose n1 = p1.new Nose();