object

面向对象编程(基础)类与对象类和对象的区别和联系属性/成员变量/字段注意事项和细节说明 栈 堆栈 方法区类和对象的内存分配机制 成员方法对定义方法语法格式的详细说明如下:1.成员方法的好处2.成员方法的定义3.返回数据类型细节成员方法传参机制基本数据类型引用数据类型递归方法重载OverLoad基本介绍 重载的好处 可变参数1.基本概念 2.基本语法 作用域生命周期构造器说明:基本介绍 细节对象创建流程案例new:this关键字案例this 的注意事项和使用细节 面对对象编程(三大特性)常用的包 访问修饰符 各种访问修饰符的访问范围封装Encapsulation封装介绍封装的理解和好处封装的实现步骤 (三步)使用原则package、import 和 import static类的继承extends类继承的定义继承给编程带来的便利 继承的深入讨论/细节问题 super (重要)变量(&方法)的查找顺序 (重要)子类创建的内存布局方法重写Override(覆盖)@Override:方法的重写和重载比较supersuper调用父类构造器super 的访问不限于直接父类,super 给编程带来的便利/细节super 和 this 的比多态Polymorphism什么是多态多[多种]态[状态]基本介绍 多态的具体体现 对象的多态 (核心,困难,重点)向上转型属性没有重写之说!属性的值看编译类型方法的调用看运行类型向下转型instanceof运算符java 的动态绑定机制(非常非常重要.)多态的应用 Object 类详解 &处理对象main方法给main传递参数toString()hashCode ()==和equals()finalize ()继承与组合面对对象编程(下)类成员(变量与方法)-static类变量概念类方法概念static初始化块(代码块)初始化块定义静态初始化块 -static初始化块和构造器初始化块、静态初始化块、构造函数的执行顺序final修饰符概念final and static不可变类(包装类) 缓存实例的不可变类 抽象类abstract概念细节*抽象类-模板设计模式java9改进的接口interface implements基本介绍细节接口vs继承接口多态特征多态传递//高聚合低耦合内部类内部类分类:(本质还是类)局部内部类匿名内部类成员内部类静态内部类单例设计模式步骤[单例模式-饿汉式]步驟[单例模式-懒汉式]分割对象与垃圾回收1.5.11.2 强制垃圾回收1.5.11.3 finalize 方法1.5.11.4 对象的软、弱和虚引用修饰符的适用范围

面向对象编程(基础)

更符合我们平时的思维习惯 把困难问题简单化 让我们从执行者变成指挥者 万物皆对象

面试就涉及面对对象,你遇到开发需求,以你实力完全可以一个人完成,但是你有更重要的事情要完成,所以需要面试一位程序员来帮你完成。你好去完成更重要的事情。

类与对象

类和对象的区别和联系

1)类是抽象的,概念的,代表一类事物,比如人类,猫类.., 即它是数据类型.

2) 对象是具体的,实际的,代表一个具体事物, 即 是实例.

3) 类是对象的模板对象是类的一个个体,对应一个实例

类是对象的抽象,对象是类的实例化

类的修饰符只能是 public 和 默认

属性/成员变量/字段

1)从概念或叫法上看: 成员变量 = 属性 = field(字段)

2) 属性是类的一个组成部分,一般是基本数据类型,也可是引用类型(对象,数组)(String常量)。 int age 就 是属性

四个基本数据类型:byte short int long 1248

两个浮点型:float double 84

一个字符型:char 2

一个布尔型:boolean 1

注意事项和细节说明

1) 属性的定义语法同变量,示例:访问修饰符 属性类型 属性名;

简单的介绍访问修饰符: 控制属性的访问范围

有四种访问修饰符 public, proctected, 默认, private , 后面会详细介绍

2) 属性的定义类型可以为任意类型,包含基本类型或引用类型

3) 属性如果不赋值,有默认值,规则和数组一致。

栈 堆栈 方法区

类和对象的内存分配机制

Java 内存的结构分析

1) 栈: 一般存放基本数据类型(局部变量) 保存的是堆内存的地址(接收对象地址值) 创建方法运行空间 方法入栈

2) 堆: 存放对象(Cat cat , 数组、成员变量等) -返回->地址 堆内存需要用new关键字来分配空间;

任何情况下,只要看见关键字new,都表示要分配新的堆内存空间,一旦堆内存空间分配了,里面就会有类中定义的属性,并且属性内容都是其对应数据类型的默认值。

3) 方法区:常量池(常量,比如字符串), 类加载信息 成员属性and成员方法(返回方法地址值)

成员属性:

局部变量:

引用类型: 引用传递的精髓:同一块堆内存空间,可以同时被多个栈内存所指向,不同的栈可以修改同一块堆内存的内容

常量: 方法区

方法: 方法在类加载时创建 在堆调用(对象用) 在栈运行

类加载: 方法区

堆、栈、方法区概念区别

1, Java 堆

    堆内存用于存放由new创建的对象和数组。在堆中分配的内存,由java虚拟机自动垃圾回收器来管理。

堆中产生了一个数组或者对象后,还可以在栈中定义一个特殊的变量,这个变量的取值等于数组或者对象在堆内存中的首地址,在栈中的这个特殊的变量就变成数组或者对象引用变量,以后就可以在程序中使用栈内存中的引用变量来访问堆中的数组或者对象,引用变量相当于为数组或者对象起的一个别名,或者代号

  根据垃圾回收机制的不同,Java堆有可能拥有不同的结构,最为常见的就是将整个Java堆分为新生代和老年代。

其中新声带存放新生的对象或者年龄不大的对象,老年代则存放老年对象。

  新生代分为den区、s0区、s1区,s0和s1也被称为rom和to区域,他们是两块大小相等并且可以互相角色的空间。

  绝大多数情况下,对象首先分配在eden区,在新生代回收后,如果对象还存活,则进入s0或s1区,之后每经过一次

  *新生代回收,如果对象存活则它的年龄就加1,对象达到一定的年龄后,则进入老年代。*

*

img

*

2,Java栈

    

    Java栈是一块线程私有的空间,一个栈,一般由三部分组成:局部变量表、操作数据栈和帧数据区

     局部变量表:用于报错函数的参数及局部变量

     操作数栈:主要保存计算过程的中间结果,同时作为计算过程中的变量临时的存储空间

     帧数据区:除了局部变量表和操作数据栈以外,栈还需要一些数据来支持常量池的解析,这里帧数据区保存着访问常量池的指针,方便计程序访问常量池,另外当函数返回或出现异常时卖虚拟机子必须有一个异常处理表,方便发送异常的时候找到异常的代码,因此异常处理表也是帧数据区的一部分。

    

img

3,Java方法区

    Java方法区和堆一样,方法区是一块所有线程共享的内存区域,他保存系统的类信息

    比如类的字段、成员属性、成员方法、常量池等。方法区的大小决定系统可以保存多少个类。如果系统

    定义太多的类,导致方法区溢出。虚拟机同样会抛出内存溢出的错误。方法区可以理解

    *为永久区。*

Java 创建对象的流程简单分析

Person p = new Person(); p.name = “jack”; p.age = 10

1) 先加载 Person 类信息(属性和方法信息, 只会加载一次)

2) 在堆中分配空间, 进行默认初始化(看规则) 全局成员给定默认值

3) 把地址赋给 p , p 就指向对象韩顺平循序渐进学 Java 零基础 第 193页

4) 进行指定初始化, 比如 p.name =”jack”p.age=10

成员方法

对定义方法语法格式的详细说明如下:

修饰符:修饰符可以省略,也可以是 public、 protected、 private、 static、 final、 abstract,其中 public 、protected、 private 三个最多只能出现其中之一 ; 以 与 static 组合起来修饰方法 。final 和 abstract 最多只能出现其中之一 ,它们可以 与 static 组合起来修饰方法 。

方法返回值类型:返回值类型可以是 Java 语 言 允许的任何数据类型,包括基本类型和引用类型;如果声明了方法返回值类型,则方法体内必须有一个有效的 return 语句,该i吾句返回 一个变量 或一个表达式,这个变量或者表达式的类型必 须与此处声 明的类型匹配 。 除此之外 , 如果一个 方法没有返回值 ,则必须使用 void 来声明没有返回值 。

方法名:方法名的命名规则与成员变量 的命名规则基本相同,但由于方法用于描述该类或该类 的实例的行为特征或功能实现,因此通常建议方法名以英文动词开头 。

形参列表:形参列表用于定义该方法可以接受的参数,形参列表由 零组到多组"参数类型形参 名"组合而成 , 多组参数之间以英文逗号 (,)隔开,形参类型和形参名之间以英文空格隔开 。 一旦在定义方法时指定了形参列表,则调用该方法时必须传入对应的参数值一 谁调用方法, 谁负责为形参赋值 。 ———————————————— 版权声明:本文为CSDN博主「David_Hernandez」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。 原文链接:Java 学习笔记_David's Notes-CSDN博客

1.成员方法的好处

1) 提高代码的复用性

2) 可以将实现的细节封装起来,然后供其他用户来调用即可

2.成员方法的定义

1) 形参列表:表示成员方法输入 cal(int n) , getSum(int num1),

2) 返回数据类型:表示成员方法输出, void 表示没有返回值

3) 方法主体:表示为了实现某一功能代码块

4) return 语句不是必须的。

5) 老韩提示: 结合前面的题示意图, 来理解

3.返回数据类型细节

  1. 一个方法最多有一个返回值 [思考,如何返回多个结果 返回数组 ]

  2. 返回类型可以为任意类型,包含基本类型或引用类型(数组,对象)

  3. 如果方法要求有返回数据类型,则方法体中最后的执行语句必须为 return 值;

补充:非静态方法内有隐藏的this关键字

在实际工作中,我们的方法都是为了完成某个功能,所以方法名要有一定含义 //,最好是见名知意

成员方法传参机制

方法在类加载时创建

在堆调用(对象用)

在栈运行

基本数据类型

引用数据类型

p=null 和 p = new Person(); 对应示意

方法体内的形参P的指向null了;并没有改变原来的指向,所以不影响实参的指向。

编写一个方法 copyPerson,可以复制一个 Person 对象,返回复制的对象。克隆对象。注意要求得到新对象和原来的对象是两个独立的对象,只是他们的属性相同

递归

简单的说: 递归就是方法自己调用自己,每次调用时传入不同的变量.递归有助于编程者解决复杂问题,同时可以让代码变 得简洁


方法重载OverLoad

基本介绍

java 中允许同一个类中,多个同名方法的存在,但要求 形参列表不一致!

  1. 方法名:必须不同

  2. 形参列表:必须不同(形参类型或个数或顺序,至少有一样不同,参数名无要求)

  3. 返回类型:无要求

重载的好处

通过参数的不同,调用到不同的同名方法

1) 减轻了起名的麻烦

2) 减轻了记名的麻烦

可变参数

1.基本概念

java 允许将同一个类中多个同名同功能但参数个数不同的方法,封装成一个方法。 就可以通过可变参数实现

  1. 可变参数的实参可以为0个或任意多个

  2. 可变参数的实参可以为数组

  3. 本质就是数组

  4. 可以和普通类型的参数一起放在形参列表,,但必须写在最后

  5. 一个形参列表只能出现一个可变参数

2.基本语法

访问修饰符 返回类型 方法名(数据类型... 形参名) { }

作用域

基本作用

局部变量不可加修饰符

生命周期

属性(全局变量)生命周期较长,伴随着对象的创建而创建,伴随着对象的销毁而销毁。

局部变量,生命周期较短,伴随着它的代码块的执行而创建, 伴随着代码块的结束而销毁。即在一次方法调用过程中

构造器

说明:

1) 构造器的修饰符可以默认, 也可以是 public protected private

2) 构造器没有返回值

3) 方法名和类名字必须一样

4) 参数列表 和 成员方法一样的规则

5) 构造器的调用, 由系统完成 new对象时

基本介绍

构造方法又叫构造器(constructor),是类的一种特殊的方法,它的主要作用是完成对新对象的初始化。它有几个特点:

1) 方法名和类名相同

2) 没有返回值

3) 在创建对象时,系统会自动的调用该类的构造器完成对象的初始化。

细节

对象创建流程

案例

new:

创造了对象,在内存开辟了空间,同时调用构造方法

this关键字

方法内最后一句默认调用this();

案例

this 的注意事项和使用细节

1) this 关键字可以用来访问本类的属性、方法、构造器

2) this 用于区分当前类的属性和局部变量

3) 访问成员方法的语法:this.方法名(参数列表);

4) 构造器内访问其他构造器 访问构造器语法:this(参数列表); 注意只能在构造器中使用(即只能在构造器中访问另外一个构造器, 必须放在第一 条语句)

5) this 不能在类定义的外部使用,只能在类定义的方法中使用。方法中调用别的方法;

6)static修饰的属于类,类加载无法访问this(super同理);(static返回内无法用this)

##############################################


面对对象编程(三大特性)

三大特性(封装,继承,多态)

package

常用的包

一个包下,包含很多的类,java 中常用的包有:

1) java.lang.* //lang 包是基本包,默认引入,不需要再引入.

2) java.util.* //util 包,系统提供的工具包, 工具类,使用 Scanner

3) java.net.* //网络包,网络开发

4) java.awt.* //是做 java

访问修饰符

基本介绍

java 提供四种访问控制修饰符号,用于控制方法和属性(成员变量)的访问权限(范围):

1) 公开级别:用 public 修饰,对外公开

2) 受保护级别:用 protected 修饰,对子类和同一个包中的类公开

3) 默认级别:没有修饰符号,向同一个包的类公开.

4) 私有级别:用 private 修饰,只有类本身可以访问,不对外公开.

子类依然继承了父类被private修饰的成员,只不过不能显式调用

各种访问修饰符的访问范围

使用的注意事项

封装Encapsulation

封装介绍

private set get

封装的理解和好处

将构造器和 setXxx 结合

//有三个属性的构造器 
public Person(String name, int age, double salary) {     // this.name = name; 
 // this.age = age; 
 // this.salary = salary; 
//我们可以将 set 方法写在构造器中,这样仍然可以验证
 setName(name); 
 setAge(age); 
 setSalary(salary); }

封装的实现步骤 (三步)

使用原则

package、import 和 import static

类的继承extends

--->接口

类继承的定义

继承基本介绍和示意图

继承可以解决代码复用,让我们的编程更加靠近人类思维.

当多个类存在相同的属性(变量)和方法时,可以从这些类中 抽象出父类,在父类中定义这些相同的属性和方法,

所有的子类不需要重新定义这些属性和方法,只需要通过 extends 来 声明继承父类即可。可直接调用所有父类非私有的成员

画出继承的示意图

继承给编程带来的便利

1) 代码的复用性提高了

2) 代码的扩展性和维护性提高了

继承的深入讨论/细节问题 super (重要)

1) 子类继承了所有的属性和方法,非私有的属性和方法可以在子类直接访问, 但是私有属性和方法不能在子类直接访 问,要通过父类提供公共的方法去访问

2) 子类必须调用父类的构造器, 完成父类的初始化 super调用不限于直接父类!将一直往上追溯直到 Object 类(顶级父类)

3) 当创建子类对象时,不管使用子类的哪个构造器,默认情况下总会去调用父类的无参构造器,如果父类没有提供无 参构造器,则必须在子类的构造器中用 super 去指定使用父类的哪个构造器完成对父类的初始化工作,否则,编译不会通过(怎么理解。) 隐式的调用super()

4) 如果希望指定去调用父类的某个构造器,则显式的调用一下 : super(参数列表)

5) super 在使用时,必须放在构造器第一行(super 只能在构造器中使用)

6) super() 和 this() 都只能放在构造器第一行,因此这两个方法不能共存在一个构造器

7) java 所有类都是 Object 类的子类, Object 是所有类的基类.

8) 子类最多只能继承一个父类(指直接继承),即 java 中是单继承机制。 思考:如何让 A 类继承 B 类和 C 类? 【A 继承 B, B 继承 C】

9) 不能滥用继承,子类和父类之间必须满足 is-a 的逻辑关系

10)super();//创造子类对象时->加载的父类属于子类内存空间

package com.hongpeng.test11;
​
public class Test13 {
    public static void main(String[] args) {
        Father f = new Father();
        Son s = new Son();                      //创造子类对象时->加载的父类属于\存在子类内存空间
        System.out.println(f.getInfo());hongpeng1
        System.out.println(s.getInfo());hongpeng2
        s.test();
        System.out.println("-----------------");5
        s.setInfo("北大青鸟");//继承来的info发生改变            
        System.out.println(f.getInfo());hongpeng6
        System.out.println(s.getInfo());北大青鸟7
        s.test();
    }
}
class Father{
    private String info = "hongpeng";
    public void setInfo(String info){
        this.info = info;
    }
    public String getInfo(){
        return info;
    }
}
class Son extends Father{
 
 {{{        son.father ———> private String info = "hongpeng";//北大青鸟
                            public void setInfo(String info){
                                this.info = info;
                            }
                            public String getInfo(){
                                return info;
                            }                                               }}}
 
    private String info = "北大青鸟牛皮";
    public void test(){
        System.out.println(this.getInfo());hongpeng3    北大青鸟8
        System.out.println(super.getInfo());hongpeng4   北大青鸟9
    }
}
=================================false  子类没有重写getInfo   会调用父类的该方法
​
-------------========-------------    
hongpeng
hongpeng
hongpeng
hongpeng
-----------------
hongpeng
北大青鸟
北大青鸟
北大青鸟
​

变量(&方法)的查找顺序 (重要)

讲解继承的本质

属性--->方法亦然

这时请大家注意,要按照查找关系来返回信息 //

(1) 首先看子类是否有该属性 //

(2) 如果子类这个属性,并且可以访问,则返回信息 。不可访问不会继续找父类,而且会报错//

(3) 如果子类没有这个属性,就看父类有没有这个属性(如果父类有该属性,并且可以访问,就返回信息..) //

(4) 如果父类没有就按照(3)的规则,继续找上级父类,直到 Object...

方法

(1) 首先看子类是否有该方法 //

(2) 如果子类这个方法,并且可以访问,则调用 。不可访问不会继续找父类,而且会报错//

*(3) 如果子类没有这个方法,就看父类有没有这个方法(如果父类有该方法,并且可以访问,就调用..所用的属性遵循就近原则父类不会找子类要数据))

(4) 如果父类没有就按照(3)的规则,继续找上级父类,直到 Object...

================

实例化子类对象:

*父类构造方法调用了被子类重写了的方法 s = new 子类();

重点在于要时刻记得子类重写父类方法,调用时会调用子类重写之后的方法

(一、)创造子类对象,子类构造方法通过super加载父类构造方法时,父类构造方法内所调用的方法优先是子类重写的方法。因为对象是s 子类 s = new 子类();

class Test06 {
 public static void main(String[] args) {
     Sub s = new Sub();
 }
}
class Base{
 Base(){
     method(100);
 }
 {
     System.out.println("base");
 }
 public void method(int i){
     System.out.println("base : " + i);
 }
}
class Sub extends Base{
 Sub(){
     super.method(70);
 }
 {
     System.out.println("sub");
 }
 public void method(int j){
     System.out.println("sub : " + j);
 }

子类创建的内存布局

方法重写Override(覆盖)

概念

细节

子类方法的返回类型和父类方法返回类型一样

者是父类的返回类型的子类

子类方法不能缩小父类方法的访问权限

@Override:

@Override:子类的fly方法重写了父类的 fly 。

1.@Override 注解在子类重写了父类的方法上,表示子类的fly方法重写了父类的 fly 。编译器会去检查该方法是否真的重写了父类的方法,如果没有构成重写,则编译错误

2.查看@Override注解类的源码为:

/*
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface Override {
}
*/

3.修饰范围:只能修饰方法,(类包属性等不可。

@Target(ElementType.METHOD),

3.@interface是注解类。

4.@Target是修饰注解的注解,称为元注解

方法的重写和重载比较

protected Object f(B b){
        return b;
}
@Override
protected B f(B b) {
    return b;
}

super

子类构造方法默认调用super() 跳转堆栈图

super能出现在实例方法和构造方法中。 super的语法是:“super.”、“super()”

在恰当的时间使用:super(实际参数列表);

注意:在构造方法执行过程中一连串调用了父类的构造方法,父类的构造方法又继续向下调用它的父类的构造方法,但是实际上对象只 创建了一个。

“super(实参)”到底是干啥的?

super(实参)的作用是:初始化当前对象的父类型特征。不是创建新对象。实际上对象只创建了1个。

创造子类对象时->加载的父类属于子类内存空间

super关键字代表什么呀?:

super关键字代表的就是“当前对象”的那部分父类型特征

super调用父类构造器

5) super 在使用时,必须放在构造器第一行(super() 只能在构造器中使用)

super能出现在实例方法构造方法

6) super() 和 this() 都只能放在构造器第一行,因此这两个方法不能共存在一个构造器

super 的访问不限于直接父类,

如果爷爷类>和本类中有同名的成员,也可以使用 super 去访问爷爷类的成员;

如果多个基类(上级类)中都有同名的成员,使用 super 访问遵循就近原则。A->B->

super 给编程带来的便利/细节

super 和 this 的比

super堆栈

public class SuperTest03{
    public static void main(String[] args){
    CreditAccount ca1 = new CreditAccount();
    System.out.println(ca1.getActno() + "," + ca1.getBalance() + "," + ca1.getCredit());
​
    CreditAccount ca2 = new CreditAccount("1111", 10000.0, 0.999);
    System.out.println(ca2.getActno() + "," + ca2.getBalance() + "," + ca2.getCredit());
​
}
}
// 账户
class Account extends Object{
    // 属性
    private String actno;
    private double balance;
// 构造方法
public Account(){
//默认有以下代码
    //super();
    //this.actno = null;
    //this.balance = 0.0;
}
public Account(String actno, double balance){
    // super();
    this.actno = actno;
    this.balance = balance;
}
​
// setter and getter
public void setActno(String actno){
    this.actno = actno;
}
public String getActno(){
    return actno;
}
public void setBalance(double balance){
    this.balance = balance;
}
public double getBalance(){
    return balance;
}
}
// 信用账户
class CreditAccount extends Account{
// 属性:信誉度(诚信值)
// 子类特有的一个特征,父类没有。
private double credit;
​
// 构造方法
// 分析以下程序是否存在编译错误????
public CreditAccount(String actno, double balance, double credit){
​
    // 私有的属性,只能在本类中访问。
    /*
    this.actno = actno;
    this.balance = balance;
    */
​
    // 以上两行代码在恰当的位置,正好可以使用:super(actno, balance);
    // 通过子类的构造方法调用父类的构造方法。
    super(actno, balance);
    this.credit = credit;
}
​
// 提供有参数的构造方法
public CreditAccount(){
    //super();
    //this.credit = 0.0;
}
​
// setter and getter方法
public void setCredit(double credit){
    this.credit = credit;
}
public double getCredit(){
    return credit;
}
————————————————
版权声明:本文为CSDN博主「一直小菜鸡~」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/qq_44715943/article/details/115916373

多态Polymorphism

什么是多态

方法形参为父类的引用,实参为子类的对象




多[多种]态[状态]基本介绍

方法或对象具有多种形态。

是面向对象的第三大特征,多态是建立在封装和继承基础之上的。

多态的具体体现

1) 方法的多态 PloyMethod.java 重写和重载就体现多态

对象的多态 (核心,困难,重点)

向上转型

父类 X = new 子类 Y();

编译类型 Xxx = 运行类型 Yyy();

属性没有重写之说!属性的值看编译类型

方法的调用看运行类型

//父类不能调用子类特有成员

向上转型调用方法的规则如下:

(1)可以调用父类中的所有成员(需遵守访问权限)

(2)但是不能调用子类的特有的成员 //(#)因为在编译阶段,能调用哪些成员,是由编译类型来决定的

(3)最终运行效果看子类(运行类型)的具体实现,

即调用方法时,按照从子类(运行类型)开始查找方法 ,然后调用,规则我前面我们讲的方法调用规则一致。

/*A a = new A(){//运行类型是继承了A的匿名类       这里相当于向上转型了    父类  A =  new  匿名类
         void a(){//重写了父类的
             System.out.println("haha");
             b();
         }
         void b(){//子类特有成员//父类不能调用
             System.out.println("aoligei");
         }
     };
     a.a();*/

向下转型

子类 Y = (子类)X;

X=父类的引用(有向上转型,才能向下转型)

instanceof运算符

比较操作符,用于判断对象的运行类型是否为 XX 类型或 XX 类型的子类型

package com.hspedu.poly_.detail_;
public class PolyDetail03 {
public static void main(String[] args) {
BB bb = new BB();
System.out.println(bb instanceof BB);// true
System.out.println(bb instanceof AA);// true
//aa 编译类型 AA, 运行类型是 BB
//BB 是 AA 子类
AA aa = new BB();
System.out.println(aa instanceof AA);
System.out.println(aa instanceof BB);
Object obj = new Object();
System.out.println(obj instanceof AA);//false
String str = "hello";
//System.out.println(str instanceof AA);
System.out.println(str instanceof Object);//true
}
}
class AA {} //父类

java 的动态绑定机制(非常非常重要.)

向上转型

多态的应用

1) 多态数组 数组的定义类型为父类类型,里面保存的实际元素类型为子类类型

Person[] persons = new Person[5];

persons[0] = new Person("jack", 20);

persons[1] = new Student("mary", 18, 100);

persons[2] = new Student("smith", 19, 30.1);

persons[3] = new Teacher("scott", 30, 20000);

persons[4] = new Teacher("king",

2) 多态参数

Worker tom = new Worker("tom", 2500);
Manager milan = new Manager("milan", 5000, 200000); PloyParameter ployParameter = new PloyParameter(); ployParameter.showEmpAnnual(tom); ployParameter.showEmpAnnual(milan); 
ployParameter.testWork(tom); 
ployParameter.testWork(milan);
​
//实现获取任何员工对象的年工资,并在 main 方法中调用该方法 [e.getAnnual()]
public void showEmpAnnual(Employee e) {
System.out.println(e.getAnnual());//动态绑定机制. }
//添加一个方法,testWork,如果是普通员工,则调用 work 方法,如果是经理,则调用 manage 方法
public void testWork(Employee e) {
if(e instanceof Worker) {
((Worker) e).work();//有向下转型操作
} else if(e instanceof Manager) {
((Manager) e).manage();//有向下转型操作
} else {
System.out.println("不做处理...");
}
}

Object 类详解 &处理对象

main方法

1) 在 main()方法中,我们可以直接调用 main 方法所在类的静态成员。

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

给main传递参数

toString()

1) 基本介绍

默认返回:全类名+@+哈希值的十六进制,【查看 Object 的 toString 方法】 子类往往重写 toString 方法,用于返回对象的属性信息

2) 重写 toString 方法,打印对象或拼接对象时,都会自动调用该对象的 toString 形式.

3) 当直接输出一个对象时,toString 方法会被默认的调用, 比如 System.out.println(monster); 就会默认调用 monster.toString()

hashCode ()

1) 提高具有哈希结构的容器的效率!

2) 两个引用,如果指向的是同一个对象,则哈希值肯定是一样的!

3) 两个引用,如果指向的是不同对象,则哈希值是不一样的

4) 哈希值主要根据地址号来的!, 不能完全将哈希值等价于地址

5) 案例演示[HashCode_.java]: obj.hashCode() [测试:A obj1 = new A(); A obj2 = new A(); A obj3 = obj1]

6) 后面在集合,中 hashCode 如果需要的话,也会重写, 在讲解集合时,老韩在说如何重写 hashCode()

==和equals()

//重写 Object 的 equals 方法 
public boolean equals(Object obj) { 
    //判断如果比较的两个对象是同一个对象,则直接返回 true 
    if(this == obj) { 
        return true; 
    } 
    //类型判断 
    if(obj instanceof Person) {
        //是 Person,我们才比较//进行 向下转型, 因为我需要得到 obj 的 各个属性 
        Person p = (Person)obj; 
        return this.name.equals(p.name) && this.age == p.age && this.gender == p.gender; 
    } //如果不是 Person ,则直接返回 false 
    return false; }

finalize ()

1) 当对象被回收时,系统自动调用该对象的 finalize 方法。子类可以重写该方法,做一些释放资源的操作

2) 什么时候被回收:当某个对象没有任何引用时,则 jvm 就认为这个对象是一个垃圾对象,就会使用垃圾回收机制来 销毁该对象,在销毁该对象前,会先调用 finalize 方法。

3) 垃圾回收机制的调用,是由系统来决定(即有自己的 GC 算法), 也可以通过 System.gc() 主动触发垃圾回收机制,测 试:Car [name] 老韩提示: 我们在实际开发中,几乎不会运用 finalize , 所以更多就是为了应付面试.

推荐的重写自定义类的equals()

继承与组合

is - a

like - a


面对对象编程(下)


类成员(变量与方法)-static

//普通属性/普通成员变量/非静态属性/非静态成员变量/实例变量

类变量概念

1.类变量也叫静态变量/静态属性,是该类的所有对象共享的变量, 2.类变量是随着类的加载创建,所以即使没有创建对象实例也可以访问 3.任何一个该类的对象去访问它时,取到的都是相同的值(同变量 同指向),同样的该类的对象去修改它时,修改的也是用一个变量。 (不管创建多少对象,用到都是用一个共享static变量)

属于类 随类加载而加载 类的对象共享 不能被覆盖

类方法概念

  1. 当方法使用了 static 修饰后,该方法就是静态方法

  2. 静态方法就可以访问静态属性/变量

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

  4. 类方法(静态方法)只能访问静态成员. .。普通成员方法,都可访问(遵循修饰)

  5. static方法中,不可以使用this 关键字

  6. 被static修饰的变量或者方法是独立于该类的任何对象,也就是说,这些变量和方法不属于任何一个实例对象,而是被类的实例对象所共享

开发自己的工具类时,可以将方法做成静态的,方便调用

static

静态成员静态调用 --类名.成员

被static修饰的变量或者方法是独立于该类的任何对象,也就是说,这些变量和方法不属于任何一个实例对象,而是被类的实例对象所共享

不能被覆盖

Java中static方法不能被覆盖,

即使在子类中已经写了个方法头一模一样的静态方法。也不是被重写,

因为静态方法是在编译时期就已经静态绑定了,所以不能被重写,

即使使用向上造型将父类引用指向子类对象,然后使用这个引用调用静态方法也是调用的父类的静态方法

1、static存在的主要意义

static的主要意义是在于创建独立于具体对象的域变量或者方法。以致于即使没有创建对象,也能使用属性和调用方法

static关键字还有一个比较关键的作用就是 用来形成静态代码块以优化程序性能。static块可以置于类中的任何地方,类中可以有多个static块。在类初次被加载的时候,会按照static块的顺序来执行每个static块,并且只会执行一次。

  为什么说static块可以用来优化程序性能,是因为它的特性:只会在类加载的时候执行一次。因此,很多时候会将一些只需要进行一次的初始化操作都放在static代码块中进行。

2、static的独特之处

1、被static修饰的变量或者方法是独立于该类的任何对象,也就是说,这些变量和方法不属于任何一个实例对象,而是被类的实例对象所共享

怎么理解 “被类的实例对象所共享” 这句话呢?就是说,一个类的静态成员,它是属于大伙的【大伙指的是这个类的多个对象实例,我们都知道一个类可以创建多个实例!】,所有的类对象共享的,不像成员变量是自个的【自个指的是这个类的单个实例对象】...我觉得我已经讲的很通俗了,你明白了咩?

2、在该类被第一次加载的时候,就会去加载被static修饰的部分,而且只在类第一次使用时加载并进行初始化,注意这是第一次用就要初始化,后面根据需要是可以再次赋值的。

3、static变量值在类加载的时候分配空间,以后创建类对象的时候不会重新分配。赋值的话,是可以任意赋值的!

4、被static修饰的变量或者方法是优先于对象存在的,也就是说当一个类加载完毕之后,即便没有创建对象,也可以去访问。

3、static应用场景

因为static是被类的实例对象所共享,因此如果某个成员变量是被所有对象所共享的,那么这个成员变量就应该定义为静态变量

因此比较常见的static应用场景有:

1、修饰成员变量 2、修饰成员方法 3、静态代码块 4、修饰类【只能修饰内部类也就是静态内部类】 5、静态导包

以上的应用场景将会在下文陆续讲到...

4、静态变量和实例变量的概念

静态变量: static修饰的成员变量叫做静态变量【也叫做类变量】,静态变量是属于这个类,而不是属于是对象。

实例变量: 没有被static修饰的成员变量叫做实例变量,实例变量是属于这个类的实例对象。

还有一点需要注意的是:static是不允许用来修饰局部变量,不要问我问什么,因为java规定的!

5、静态变量和实例变量区别【重点常用】

静态变量: 静态变量由于不属于任何实例对象,属于类的,所以在内存中只会有一份,在类的加载过程中,JVM只为静态变量分配一次内存空间。

实例变量: 每次创建对象,都会为每个对象分配成员变量内存空间,实例变量是属于实例对象的,在内存中,创建几次对象,就有几份成员变量。

我相信各位智商都比宜春智商要高,应该都能理解上面的话。下面举了例子完全出于娱乐,理解了大可不必看,下面的例子仅供参考,仅供娱乐一下下气氛,赶时间的熊dei大可略过!

怎么理解呢?打个比喻吧...就比方说程序员小王是一个比较温柔阳光的男孩子,这1024的这一天,老板闲的没事,非要拉着程序员小王来玩耍,怎么个玩法呢?老板和小王一人拿着一把菜刀,规则很简单,互相伤害,一人一刀,你一刀,我一刀....游戏一开始,老板二话不说,跳起来就是一刀,程序员小王二话也没说反手就是一菜刀回去,这个时候老板发飙了,双眼瞪得忒大,跳起来又是一刀,这个时候程序员小王不敢还手了,就没动手。没想到老板越来越生猛,左一刀右一刀全程下来差不多砍个半个时....程序员小王一直没有还过手,因为小王知道他是老板...

这个程序员小王只会在老板第一次挥刀的时候,回老板一刀,之后就不还手了,这个时候我们把程序员小王看做是静态变量,把老板第一次向小王挥刀看做是类加载,把小王回老板一刀看出是分配内存空间,而一人一刀这个回合过程看成是类加载的过程,之后老板的每一刀都看成是创建一次对象。

连贯起来就是static变量值在类第一次加载的时候分配空间,以后创建类对象的时候不会重新分配

之后这个老板挨了一刀之后躺医院了一年,一出院回到公司第一件事就是拉程序员宜春出来玩耍,老板殊不知其然,这个博主程序员宜春性格异常暴躁,老板递给程序员宜春一把菜刀,博主宜春一接过菜刀,猝不及防的被老板跳起来就是一刀,程序员宜春痛的嗷了一声,暴躁的程序员宜春还没嗷完,在嗷的同时跳起来就是给老板一刀,接着老板跳起来又是一刀,程序员宜春嗷的一声又是回一刀,老板跳起来又一刀,程序员宜春嗷的一声又是回一刀,只要老板没停程序员宜春就没停,因为程序员宜春知道,就自己这曝脾气,暴躁起来si都敢摸,肯定有几个老铁知道....

程序员宜春就类似实例变量,每次创建对象,都会为每个对象分配成员变量内存空间,就像老板来一刀,程序员宜春都会回一刀这样子的...

6、访问静态变量和实例变量的两种方式

我们都知道静态变量是属于这个类,而不是属于是对象,static独立于对象。

但是各位有木有想过:静态成员变量虽然独立于对象,但是不代表不可以通过对象去访问,所有的静态方法和静态变量都可以通过对象访问【只要访问权限足够允许就行

回过头再去品味一下上面的那段话,你就能非常客观明了了,这个思想概念要有只是这种用法不推荐

7、static静态方法

static修饰的方法也叫做静态方法,不知道各位发现咩有,其实我们最熟悉的static静态方法就是main方法了小白童鞋:喔好像真的是哦。由于对于静态方法来说是不属于任何实例对象的,this指的是当前对象,因为static静态方法不属于任何对象,所以就谈不上this了。

还有一点就是:构造方法不是静态方法

8、static静态代码块

先看个程序吧,看看自个是否掌握了static代码块,下面程序代码继承关系为

以上仅仅是让各位明确代码块之间的运行顺序,显然还是不够的,静态代码块通常用来对静态变量进行一些初始化操作,比如定义枚举类,代码如下:

public enum WeekDayEnum {
   MONDAY(1,"周一"),
    TUESDAY(2, "周二"),
    WEDNESDAY(3, "周三"),
    THURSDAY(4, "周四"),
   FRIDAY(5, "周五"),
    SATURDAY(6, "周六"),
   SUNDAY(7, "周日");
    
   private int code;
    private String desc;
   
    WeekDayEnum(int code, String desc) {
        this.code = code;
        this.desc = desc;
   }
    private static final Map<Integer, WeekDayEnum> WEEK_ENUM_MAP = new HashMap<Integer, WeekDayEnum>();
​
    // 对map进行初始化
    static {
        for (WeekDayEnum weekDay : WeekDayEnum.values()) {
           WEEK_ENUM_MAP.put(weekDay.getCode(), weekDay);
        }
   }
    public static WeekDayEnum findByCode(int code) {
       return WEEK_ENUM_MAP.get(code);
    }
​

当然不仅仅是枚举这一方面,还有我们熟悉的单例模式同样也用到了静态代码块,如下:

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

9、static变量与普通变量区别

static变量也称作静态变量,静态变量和非静态变量的区别是:静态变量被所有的对象所共享,在内存中只有一个副本,它当且仅当在类初次加载时会被初始化。而非静态变量是对象所拥有的,在创建对象的时候被初始化,存在多个副本,各个对象拥有的副本互不影响。

还有一点就是static成员变量的初始化顺序按照定义的顺序进行初始化。

10、静态内部类

静态内部类与非静态内部类之间存在一个最大的区别,我们知道非静态内部类在编译完成之后会隐含地保存着一个引用,该引用是指向创建它的外围内,但是静态内部类却没有。没有这个引用就意味着:

1、它的创建是不需要依赖外围类的创建。 2、它不能使用任何外围类的非static成员变量和方法。

代码举例(静态内部类实现单例模式)

public class Singleton {
    
   // 声明为 private 避免调用默认构造方法创建对象
    private Singleton() {
    }
    
   // 声明为 private 表明静态内部该类只能在该 Singleton 类中被访问
    private static class SingletonHolder {
        private static final Singleton INSTANCE = new Singleton();
    }
​
   public static Singleton getUniqueInstance() {
        return SingletonHolder.INSTANCE;
   }
}

Singleton 类加载时,静态内部类 SingletonHolder 没有被加载进内存。只有当调用 getUniqueInstance()方法从而触发 SingletonHolder.INSTANCESingletonHolder 才会被加载,此时初始化 INSTANCE 实例,并且 JVM 能确保 INSTANCE 只被实例化一次。

这种方式不仅具有延迟初始化的好处,而且由 JVM 提供了对线程安全的支持。

11、静态导包

静态导包格式:import static

这两个关键字连用可以指定导入某个类中的指定静态资源,并且不需要使用类名调用类中静态成员,可以直接使用类中静态成员变量和成员方法

//  Math. --- 将Math中的所有静态资源导入,这时候可以直接使用里面的静态方法,而不用通过类名进行调用
//  如果只想导入单一某个静态方法,只需要将换成对应的方法名即可
 
import static java.lang.Math.;
//  换成import static java.lang.Math.max;具有一样的效果
​
public class Demo {
    public static void main(String[] args) {
 
        int max = max(1,2);
        System.out.println(max);
    }
}

静态导包在书写代码的时候确实能省一点代码,可以直接调用里面的静态成员,但是会影响代码可读性,所以开发中一般情况下不建议这么使用。

12、static注意事项

1、静态只能访问静态。 2、非静态既可以访问非静态的,也可以访问静态的。

13、final与static的藕断丝连

到这里文章本该结束了的,但是static的使用始终离不开final字眼,二者可谓藕断丝连,常常繁见,我觉得还是很有必要讲讲,那么一起来看看下面这个程序吧。

package Demo;
​
class FinalDemo {
    public final double i = Math.random();
    public static double t = Math.random();
}
​
public class DemoDemo {
    public static void main(String[] args) {
​
        FinalDemo demo1 = new FinalDemo();
        FinalDemo demo2 = new FinalDemo();
        System.out.println("final修饰的  i=" + demo1.i);
        System.out.println("static修饰的 t=" + demo1.t);
        System.out.println("final修饰的  i=" + demo2.i);
        System.out.println("static修饰的 t=" + demo2.t);
​
        System.out.println("t+1= "+ ++demo2.t );
//      System.out.println( ++demo2.i );//编译失败
      }
}
运行结果:
    final修饰的  i=0.7282093281367935
    static修饰的 t=0.30720545678577604
    final修饰的  i=0.8106990945706758
    static修饰的 t=0.30720545678577604
    t+1= 1.307205456785776

static修饰的变量没有发生变化是因为static作用于成员变量只是用来表示保存一份副本,其不会发生变化。怎么理解这个副本呢?其实static修饰的在类加载的时候就加载完成了(初始化),而且只会加载一次也就是说初始化一次,所以不会发生变化!

初始化块(代码块)

初始化块定义

  1. 普通的代码块,在创建对象实例时,会被隐式的调用。

    不管调用哪个构造器,创建对象,都会先调用代码块的内容。

    被创建一次,就会调用一次。

  2. 代码块调用的顺序优先于构造器..

  3. 提高代码复用性

//类被加载的情况举例
//1. 创建对象实例时(new)
// AA aa = new AA();
//2. 创建子类对象实例,父类也会被加载, 而且,父类先被加载,子类后被加载
// AA aa2 = new AA();
//3. 使用类的静态成员时(静态属性,静态方法)

静态初始化块 -static

  1. static代码块,是在类加载时,执行的,而且只会执行一次.

  2. 使用类的静态成员时,静态代码块会被调用,普通代码块并不会执行

初始化块和构造器

构造器(){
    super();
    调用普通代码块;
    .......
}

初始化块、静态初始化块、构造函数的执行顺序

类加载----

1->(父类优先)静态代码块|静态初始化->(子类的静态)

2->(父类优先)普通代码块|普通初始化->构造器

3->(子类的)普通代码块|普通初始化->构造器

类初始化阶段,先执行最顶层父类的静态初始化块,然后依次向下,直到执行当前类的静态初始化块;

对象初始化阶段,先执行最顶层父类的初始化块、最顶层父类的构造器,然后依次向下,直到执行当前的初始化块、当前类的构造器。

super

this

final修饰符

概念

final--->最终

  1. 属性--不能被修改 (修饰常量 TAX_RATE)

    系统不会对final成员变量进行默认初始化

  2. 类---不能被继承 (可以实例化对象)

  3. 方法---不能被重写 (仍然遵守继承的机制)

final and static

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

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

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

class BBB {
    public final static  int num = 10000;
    static {
        System.out.println("BBB 静态代码块被执行");
    }
}

此时调用静态属性num ,不会导致BBB类加载

不可变类(包装类)

不可变( immutable ) 类的意思是创建该类的实例后, 该实例 的实例变量是不可改变的 。 Java 提供 的 8 个包装类和 java.lang.String 类都是不可变类 , 当创建它们 的实例后 , 其实例的 实例变量不可改变 。

Integer i = new Integer(); i的指向不能被改变如:i=i1

创建自定义的不可变类,需要遵守如下规则 :

使用 private 和 final 修饰符来修饰该类的成员变量。 提供带参数构造器,用于根据传入参数来初始化类里的成员变量 。 仅为该类的成员变量提供 getter 方法,不要为该类的成员变量提供 setter 方法 ,因为普通方法无法修改 final 修饰的成员变量。 如果有必要,重写 Object 类的 hashCode()和 equal()方法。equal()方法根据关键成员变量来作为两个对象是否相等的标准,除此之外,还应该保证 两个用 equals()方法判断为相等的对象的 hashCode()也相等 。

public class Address
{
	private final String detail;
	private final String postCode;
	// 在构造器里初始化两个实例变量
	public Address()
	{
		this.detail = "";
		this.postCode = "";
	}
	public Address(String detail , String postCode)
	{
		this.detail = detail;
		this.postCode = postCode;
	}
	// 仅为两个实例变量提供getter方法
	public String getDetail()
	{
		return this.detail;
	}
	public String getPostCode()
	{
		return this.postCode;
	}
	//重写equals()方法,判断两个对象是否相等。
	public boolean equals(Object obj)
	{
		if (this == obj)
		{
			return true;
		}
		if(obj != null && obj.getClass() == Address.class)
		{
			Address ad = (Address)obj;
			// 当detail和postCode相等时,可认为两个Address对象相等。
			if (this.getDetail().equals(ad.getDetail())
				&& this.getPostCode().equals(ad.getPostCode()))
			{
				return true;
			}
		}
		return false;
	}
	public int hashCode()
	{
		return detail.hashCode() + postCode.hashCode() * 31;
	}
}

缓存实例的不可变类

不可变类的实例状态不可改变,可以很方便地被多个对象所共享。如果程序经常需要使用相同的不可变类实例,则 应该考虑缓存这种不可变类的实例 。 毕竟重复创建相同的对象没有太大的 意义,而且加 大系统开销 。 如果可能,应该将已经创建的不可变类的实例进行缓存 。

缓存是软件设计中 一个非常有用的模式,缓存的实现方式有很多种,不同的实现方式可能存在较大 的性能差别,关于缓存的性能问题此处不做深入讨论 。

class CacheImmutale {
	private static int MAX_SIZE = 10;
	// 使用数组来缓存已有的实例
	private static CacheImmutale[] cache
		= new CacheImmutale[MAX_SIZE];
	// 记录缓存实例在缓存中的位置,cache[pos-1]是最新缓存的实例
	private static int pos = 0;
	private final String name;
	private CacheImmutale(String name) {
		this.name = name;
	}
	public String getName() {
		return name;
	}
	public static CacheImmutale valueOf(String name) {
		// 遍历已缓存的对象,
		for (int i = 0 ; i < MAX_SIZE; i++) {
			// 如果已有相同实例,直接返回该缓存的实例
			if (cache[i] != null
				&& cache[i].getName().equals(name)) {
				return cache[i];
			}
		}
		// 如果缓存池已满
		if (pos == MAX_SIZE) {
			// 把缓存的第一个对象覆盖,即把刚刚生成的对象放在缓存池的最开始位置。
			cache[0] = new CacheImmutale(name);
			// 把pos设为1
			pos = 1;
		} else {
			// 把新创建的对象缓存起来,pos加1
			cache[pos++] = new CacheImmutale(name);
		}
		return cache[pos - 1];
	}
	public boolean equals(Object obj) {
		if(this == obj) {
			return true;
		}
		if (obj != null && obj.getClass() == CacheImmutale.class) {
			CacheImmutale ci = (CacheImmutale)obj;
			return name.equals(ci.getName());
		}
		return false;
	}
	public int hashCode() {
		return name.hashCode();
	}
}
public class CacheImmutaleTest {
	public static void main(String[] args) {
		CacheImmutale c1 = CacheImmutale.valueOf("hello");
		CacheImmutale c2 = CacheImmutale.valueOf("hello");
		// 下面代码将输出true
		System.out.println(c1 == c2);
	}
}

抽象类abstract

父类方法不确定性的问题,考虑将该方法设计为抽象(abstract)方法

概念

  1. 所谓抽象方法就是没有实现的方法 ->所谓没有实现就是指,没有方法体

  2. 当一个类中存在抽象方法时,需要将该类声明为 abstract 类

  3. 一般来说,抽象类会被继承,有其子类来实现抽象方法

细节

  1. 抽象类,不能被实例化

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

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

  3. abstract 只能修饰类和方法,不能修饰属性和其它的

  4. 抽象方法不能使用private、final 和 static来修饰,因为这些关键字都是和重写相违背

  5. 如果一个类继承了抽象类,则它必须实现抽象类的所有抽象方法除非自己也声明为abstract接口一样

  6. 所谓实现方法,就是有方法体

  7. 抽象类的本质还是类,所以可以有类的各种成员

  8. 子类实现了抽象类,要么实现所有抽象方法,要么自己也声明为抽象类

  9. 设计模式和框架使用多,常问

*抽象类-模板设计模式

* 抽象类-模板设计模式
package com.hl.javag.abstract_.abstract04;
/*
* 抽象类-模板设计模式
* */
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));
    }
}
//AA和BB一样
public class AA extends Template {
    public void job() { //实现Template的抽象方法job
        long num = 0;
        for (long i = 1; i <= 800000; i++) {
            num += i;
        }
    }
}
public class BB extends Template{
    public void job() {//这里也去,重写了Template的job方法
        long num = 0;
        for (long i = 1; i <= 80000; i++) {
            num *= i;
        }
    }
}
public class TestTemplate {
    public static void main(String[] args) {
        AA aa = new AA();
        aa.calculateTime(); //这里还是需要有良好的OOP基础,对多态
        BB bb = new BB();
        bb.calculateTime();
    }
}

java9改进的接口interface implements

基本介绍

1.接口就是给出一些没有实现的方法封装到一起,到某个类要使用时,再根据具体情况把这些方法写出来。

interface 接口

类实现 implements 接口

2.接口是更加抽象的抽象类,抽象类里的方法可以有方法体,接口里的所有方法都没有方法体【jdk7.0】。

接口体现了程序设计的多态和高内聚低耦合的设计思想

||||||||||||||||||||||看下面的字|||||||||||||||||||||||||||||||||||||||||

【jdk8.0】后接口可以有静态方法,默认方法。

jdk1.8 默认方法: default修饰 允许在已有的接口中添加新方法,而同时又保持了与旧版本代码的兼容性, 默认方法与抽象方法不同之处在于抽象方法必须要求实现,但是默认方法则没有要求实现, 相反,接口提供了一个默认实现,这样所有的接口实现者将会默认继承他(如果有必要的话,可以覆盖这个默认实现)。 接口的默认方法:得到接口的实现类对象,直接用对象的引用.方法名。默认方法可以被实现类覆盖。default

public default void default(){}

jdk1.8 静态方法: 允许在已有的接口中添加静态方法,接口的静态方法属于接口本身,不被继承,也需要提供方法的实现。

public static void staticT(){}

jdk1.9 私有方法

private default void defaultMethod(String str){
System.out.println("InterfaceJDK8:" + str);
}

细节

  1. 接口不能被实例化 一个类可实现多个接口 接口不能继承类可以继承多个接口

  2. 接口中的属性都是public static final修饰的。接口中属性的访问形式:接口名 . 属性名

  3. 接口中所有方法都是publicabstract的,接口中的抽象方法可以不用abstract修饰。

  4. 一个普通类实现接口,就必须实现该接口类的所有方法。实现抽象也一样

  5. 接口修饰符,同类一样

    jdk1.8

  6. 接口的默认方法:得到接口的实现类对象,直接用对象的引用.方法名。默认方法可以被实现类覆盖。default

  7. 允许在已有的接口中添加静态方法,接口的静态方法属于接口本身,不被继承,也需要提供方法的实现。

接口vs继承

继承:is--a 接口:like--a

当子类继承了父类,就自动的拥有父类的功能

如果子类需要扩展功能,可以通过实现接口的方式扩展.

可以理解 实现接口是 对 java 单继承机制的一种补充.

继承的价值主要在于:解决代码的复用性和可维护性

接口的价值主要在于:设计好各种规范(方法),让其他类去实现这些方法。更加灵活。

接口多态

许多都同继承多态相似

不同其一:不能被实例化

特征

接口回溯(向上转型)接口 = new 对象

IF if01 = new Monster();

向下转型??

((Phone_) usbs[i]).call();

public class InterfacePolyArr {
    public static void main(String[] args) {

        //多态数组 -> 接口类型数组
        Usb[] usbs = new Usb[2];
        usbs[0] = new Phone_();//接口回溯
        usbs[1] = new Camera_();
        /*
        给Usb数组中,存放 Phone  和  相机对象,Phone类还有一个特有的方法call(),
        请遍历Usb数组,如果是Phone对象,除了调用Usb 接口定义的方法外,
        还需要调用Phone 特有方法 call
         */
        for(int i = 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("相机工作中...");
    }
}

接口的多态体现

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 {}

多态传递

接口B extends 接口A

类C implements B

类C--im--》A

/**
 * 演示多态传递现象
 */
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() {
    }
}

//高聚合低耦合 

package com.hl11.homework;
public class hw6 {
 public static void main(String[] args) {
     Person p = new Person("唐僧",new Horse());
     p.common();
     p.passRiver();
     p.passFireHill();
 }
}
//出行的人

//有Person类,有name和Vehicles属性,在构造器中为两个属性赋值
class Person {
private String name;
private Vehicles vehicles;

 //在创建人对象时,事先给他分配一个交通工具
 public Person(String name, Vehicles vehicles) {
  this.name = name;
     this.vehicles = vehicles;
 }

 //实例化Person对象“唐僧”,要求一般情况下用Horse作为交通工具,遇到大河时用Boat作为交通工具
 //这里涉及到一个编程思路,就是可以把具体的要求,封装成方法-> 这里就是编程思想
//思考一个问题,如何不浪费,在构建对象时,传入的交通工具对象->动脑筋
 public void passRiver() {
     //先得到船
     //判断一下,当前的 vehicles 属性是null, 就获取一艘船
//        Boat boat = VehiclesFactory.getBoat();
//        boat.work();
     //如何防止始终使用的是传入的马 instanceOf
  //if (vehicles == null) {
  //vehicles instanceof Boat 是判断 当前的 vehicles是不是Boat
     //(1) vehicles = null  : vehicles instanceof Boat  => false
     //(2) vehicles = 马对象 :vehicles instanceof Boat  => false
     //(3) vehicles = 船对象 :vehicles instanceof Boat  => true
     if (!(vehicles instanceof Boat)) {
         vehicles = VehiclesFactory.getBoat();
     }
     vehicles.work();
 }

 public void common() {
     //得到马儿
  //判断一下,当前的 vehicles 属性是null, 就获取一匹马
     //if (vehicles == null) {
     if (!(vehicles instanceof Horse)) {
         //这里使用的是多态
         vehicles = VehiclesFactory.getHorse();
     }
     //这里体现使用接口调用
     vehicles.work();
 }
 //过火焰山
 public void passFireHill() {
     if (!(vehicles instanceof Planes)) {
         //这里使用的是多态
         vehicles = VehiclesFactory.getPlanes();
     }
     //这里体现使用接口调用
     vehicles.work();

 }
}



//出行工具工场
//目的应该是规范交通对象
//马儿始终是同一匹
class VehiclesFactory {
//马儿始终是同一匹
private static Horse horse = new Horse(); //饿汉式

 private VehiclesFactory(){}
 //创建交通工具工厂类,有两个方法分别获得交通工具Horse和Boat
//这里,我们将方法做成static
 public static Horse getHorse() {
//        return new Horse();
     return horse;
 }
public static Boat getBoat() {
     return new Boat();
 }
 public static Planes getPlanes() {
     return new Planes();
 }
}
//不同出行工具不同功能
class Horse implements Vehicles{
@Override
public void work() {
  System.out.println("一般使用马匹");
 }
}
class Boat implements Vehicles{
 @Override
public void work() {
  System.out.println("过河使用小船");
 }
}
class Planes implements Vehicles{
 @Override
public void work() {
  System.out.println("过火焰山,使用飞机");
 }
}
//出行工具的功能
interface Vehicles{
void work();
}

内部类

内部类分类:(本质还是类)

但是成为了外部类的一个成员

定义在外部类的局部位置(方法中/代码块) :

(1) 局部内部类 -->class A{ public void ff(){ class B{//我是局部内部类}}}

(2) 匿名内部类 -->class A{ public void ff(){B b = new B(){ }//我是匿名内部类}} interface B{} class A$1 implements B{}

class A{ public void ff(){B b = new B(){ }//我是匿名内部类}} class B{} class A$2 extends B{}

class A{ public void ff(){B b = new B(){ }//我是匿名内部类}} abstract class B{} class A$3 extends B{}抽象 必须实现父类所有抽象方法

定义在外部类成员位置:

(1) 成员内部类 -->class A{ class B{} }

(2) 静态内部类 -->class A{ static class B{} }

局部内部类

  1. 局部内部类是定义在外部类的局部位置,通常在方法

  2. 可以直接访问外部类的所有成员,包含私有的(方法体内访问)

  3. 不能添加访问修饰符,但是可以使用final 修饰

  4. 作用域 : 仅仅在定义它的方法或代码块中(如局部变量)

  5. 外部类在方法中可以创建局部内部类对象,然后调用方法即可(看2(局部内部类里的方法)

    //外部类在方法中,可以创建Inner02对象,然后调用方法即可
    void 外部类的方法(){
        局部内部类{   
        }
        局部内部类 inner02 = new 局部内部类();
        inner02.f1();
    }
  6. 如果外部类和局部内部类的成员重名时,默认遵循就近原则,如果想访问外部类的成员,使用 (外部类名.this.成员)去访问 解读 Outer02.this 本质就是外部类的对象,

    哪个对象调用了m1, Outer02.this就是哪个对象

  7. 匿名内部类也是属于局部内部类

package com.hl.javag.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();
    }

}

匿名内部类

概念:

1.需求: 想使用IA接口,并创建对象

2.传统方式,是写一个类,实现该接口,并创建对象

3.老韩需求是 Tiger/Dog 类只是使用一次,后面再不使用

4.可以使用匿名内部类来简化开发

5.编译类型 ? IA接口(类)对象

6.运行类型 ? 就是匿名内部类(实现了,继承了接口(类)) Outer04$1

就是继承了该类的子类

7.jdk底层在创建匿名内部类 Outer04$1,立即马上就创建了 Outer04$1实例,并且把地址返回给 tiger

局部内部类

8.匿名内部类使用一次,就不能再使用 (new Outer04$1().cry();错误)、

9.外部其他类---不能访问----->匿名内部类(匿名内部类属于方法成员:“作用域”)

外部类需要访问匿名内部类

变量名.重写的方法

如果需要新建方法,需要在重写方法里面调用核心方法

10.可以直接访问外部类的所有成员,包含私有的

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

基于接口、类、抽象类

public  calss AnonymousInnerClass{//外部其他类
	public static void main(String[] args){
		Outer o = new Outer();
		o.method();
		f1(new Inner(){
			@Override
			public void show(){
					sout
		}
		});
	}
}
class Outer04 { //外部类
private int n1 = 10;//属性
public void method() {//方法
		Inner i = new Inner(){};//匿名内部类---->class Outer04$1 implements inner{};
i.方法
或者直接.方法
		Student s = new Student(){};//匿名内部类---->class Outer04$1 etxends inner{};
		Animal a = new Animal(){};//匿名内部类---->class Outer04$1 etxends inner{};
	}
}

interface inner {}//接口
class Student{}//外部其他类
abstract class Animal {}//抽象类

package com.hl.javag.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. 匿名内部类使用一次,就不能再使用 x   (new Outer04$1().cry();错误)
        IA tiger = new IA() {
            @Override
            public void cry() {
                System.out.println("老虎叫唤...");
            }
        };
        System.out.println("tiger的运行类型=" + tiger.getClass());
        tiger.cry();
        tiger.cry();
        tiger.cry();
//        new Outer04$1().cry();

//        IA tiger = 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 = new Animal(){
            @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();
}
package com.hl.javag.innerclass;

public class AnonymousInnerClassDetail {
    public static void main(String[] args) {

        Outer05 outer05 = new Outer05();
        outer05.f1();
        //外部其他类---不能访问----->匿名内部类
        System.out.println("main outer05 hashcode=" + outer05);
        Person person = new Person(){
            @Override
            public void hi() {
                System.out.println("hhhh");
            }
        };
        person.hi();
    }
}

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("Person ok() " + str);
    }
}
//抽象类/接口...

成员内部类

  1. 可以直接访问外部类的所有成员(包含私有的)

  2. 可以添加任意访问修饰符,(地位就是一个类的成员

  3. 作用域和外部类的其他成员一样,为整个类(外部类)体

  4. 成员内部类 访问 外部类成员

    直接访问

  5. 外部类 访问 成员内部类

    public void t1() {//外部类里创造方法
            //使用成员内部类
            //创建成员内部类的对象,然后使用相关的方法
            Inner08 inner08 = new Inner08();
            inner08.say();
            System.out.println(inner08.sal);
        }
  6. 外部其他类 访问 成员内部类

    Outer08 o = new Outer08();
    // 第一种方式
    // outer08.new Inner08(); 相当于把 new Inner08()当做是 outer08 成员
    // 这就是一个语法,不要特别的纠结. 
    Outer08.Inner08 inner08 = o.new Inner08();
    inner08.say();
    // 第二方式 在外部类中,编写一个方法,可以返回 Inner08 对象
    Outer08.Inner08 inner08s = o.getInner08Instance();
    inner08s.say();
    
    //方法,返回一个Inner08实例
        public Inner08 getInner08Instance(){//外部类里的方法
            return new Inner08();
        }

  7. 如果外部类和局部内部类的成员重名时,默认遵循就近原则,如果想访问外部类的成员,使用 (外部类名.this.成员)去访问

package com.hl.javag.innerclass;

public class MemberInnerClass01 {
 public static void main(String[] args) {
     Outer08 o = new Outer08();
     outer08.t1();
     //外部其他类,使用成员内部类的三种方式
     //老韩解读
     // 第一种方式
     // outer08.new Inner08(); 相当于把 new Inner08()当做是outer08成员
     // 这就是一个语法,不要特别的纠结.
     Outer08.Inner08 inner08 = o.new Inner08();
     inner08.say();
     // 第二方式 在外部类中,编写一个方法,可以返回 Inner08对象
     Outer08.Inner08 inner08Instance = o.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);
 }
}

静态内部类

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

  2. 可以添加任意访问修饰符(public,protected,默认,private),

    地位就是一个类的成员

  3. 作用域和外部类的其他成员一样,为整个类(外部类)体

  4. 静态内部类 访问 外部类成员

    直接访问所有静态成员

  5. 外部类 访问 静态内部类

    public void m1() { // 外部类里定义方法。创建对象,再访问
    Inner10 inner10 = new Inner10();
    inner10.say();
    }
  6. 外部其他类 访问 静态内部类

    //方式 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();
    
    public Inner10 getInner10() {
    return new Inner10();
    }
    public static Inner10 getInner10_() {
    return new Inner10();
    }

    与其他不一样之处,因为是静态,不需要this.

  7. 如果外部类和局部内部类的成员重名时,默认遵循就近原则,如果想访问外部类的成员,使用 (外部类名.成员)去访问

package com.hl.javag.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 = "韩顺平教育";
     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. 将构造器私有化(防止被直接new)

  2. 在类的内部直接创建对象(该对象是static private)

  3. 提供一个公共的static方法,返回 gf对象(通过方法方回对象)

對象,通常是重量級的對象, 餓漢式可能造成創建了對象,但是沒有使用.

package com.hl.javag.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);
//        GirlFriend.ce();
        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;
    }
//============================================
    private static void sf(){
        System.out.println("私有方法");
    }
    public static void ce() {
        sf();
    }

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

步驟[单例模式-懒汉式]

1.仍然构造器私有化(为了不被直接new对象) ​ 2.定义一個static静态属性对象(不是静态就需要创造对象了,没意义) ​ 3.提供一個public的static方法,可以返回一個Cat對象(方法条件创造对象) ​ 4.懒汉式,只有当用户使用getInstance時,才返回cat對象, 后面再次调用,会返回上次创建的cat对象,從而保证了單例(使用方法时才会创造唯一对象)有bug(线程)

package com.hl.javag.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 + '\'' +
                '}';
    }
}

分割

对象与垃圾回收

垃圾回收机制具有如下特征:

垃圾回收机制只负责回收堆内存中的对象,不会回收任何物理资源(例如数据库连接、网络 IO 等资源) 。 程序无法精确控制垃圾回收的运行,垃圾回收会在合适的时候进行。当对象永久性地失去引用后,系统就会在合适的时候回收它所占的内存 。 在垃圾回收机制回收任何对象之前,总会先调用它的 finalize() 方法,该方法可能使该对象重新复活(让一个引用变量重新引用该对象) ,从而导致垃圾回收机制取消回收。 1.5.11.1 对象在内存中的状态 当 一个对象在堆内存中运行时,根据它被引用变量所引用的状态,可以把它所处的状态分成如下三种。

可达状态 : 当 一个对象被创建后,若有一个以上的引用变量引用它,则这个对象在程序中处于 可达状态,程序可通过引用变量来调用该对象的实例变量和方法。 可恢复状态:如果程序中某个对象不再有任何引用变量引用它 ,它就进入了可恢复状态。在这种状态下,系统的垃圾回收机制准备回收该对象所占用的内存,在回收该对象之前,系统会调用所有可恢复状态对象的 finalize() 方法进行资源清理 。 如果系统在调用 finalize() 方法时重新让 一个引用变量引用该对象,则这个对象会再次变为可达状态;否则该对象将进入不可达状态。 不可达状态:当对象与所有引用变量的关联都被切断,且系统已经调用所有对象的 finalize() 方法后依然没有使该对象变成可达状态,那么这个对象将永久性地失去引用,最后变成不可达状态。只有当 一个对象处于不可达状态时,系统才会真正回收该对象所占有的资源。

1.5.11.2 强制垃圾回收

当 一个对象失去引用后,系统何时调用它的 finalize() 方法对它进行资源清理,何时它会变成不可达 状态,系统何时回收它所占有的内存,对于程序完全透明 。

程序无法精确控制 Java 垃圾回收的 时机 ,但依然可以强制系统进行垃圾回收一一这种 强制只是通 知系统进行垃圾回收,但系统是否进行垃圾回收依然不确定 。 大部分时候,程序强制系 统垃圾回 收后总 会有一些效果 。 强制系统垃圾回收有如下两种方式 。

调用 System 类的 gc() 静态方法: System.gc()。 调用 Runtime 对象的 gc() 实例方法: Runtime.getRuntimeO.gc()。 运行 Java 命令时指定-verbose:gc 选项,可以看到 每次垃坡回收后的提示信息

1.5.11.3 finalize 方法

在垃圾回收机制回收某个对象所占用的内存之前,通常要求程序调用适当的方法来清理资源 , 在没有明确指定清理资源的情况下, Java 提供了默认机制来清理该对象的资源 , 这个机制就是 finalizeO方法。 该方法是定义在 Object 类里的实例方法,方法原型为 :

protected void finalize() throws Throwable

当 finalize() 方法返回后,对象消失,垃圾回收机制开始执行。方法原型中 的 throws Throwable 表示 它可以抛出任何类型的异常。

任何 Java 类都可以重写 Object 类的 finalizeO方法,在该方法中清理该对象占用的资源 。 如果程序 终止之前始终没有进行垃圾回收 ,则 不会调用失去引用对象的 finalizeO方法来清理资源。垃圾回 收机制 何时调用对象的 finalizeO方法是完全透明的,只有当程序认为需要更多的额外内存时,垃圾回收机制才 会进行垃圾回收 。 因此,完全有可能出现这样一种情形:某个失去引用的对象只占用了少量内存,而且 系统没有产生严重的内存需求,因此垃圾回收机制并没有试图回收该对象所占用的资源,所以该对象的 自nalizeO方法也不会得到调用。

finalize() 方法具有如下 4 个特 点。

永远不要主动调用某个对象的 finalize() 方法,该方法应交给垃圾回收机制调用 。 finalize() 方法何时被调用,是否被调用具有不确定性,不要把 finalize() 方法当成一定会被执行的方法 。 当 JVM 执行可恢复对象的 finalize() 方法时,可能使该对象或系统中其他对象重新变成可达状态。 当 JVM 执行 finalize() 方法时出现异常时,垃圾回收机制不会报告异常,程序继续执行 。 由于 finalizeO方法并不一定会被执行,因此如果想清理某个类里打开的资源,则不要放在 finalize() 方法中进行清理。

1.5.11.4 对象的软、弱和虚引用

Java 语言对对象的引用有如下 4 种方式:

强引用 (StrongReference)

这是 Java 程序中最常见的引用方式 。 程序创建一个对象,并把这个对象赋给一个引用变量 ,程序通过该引用变量来操作实际的对象,前面介绍的对象和数组都采用了这种强引用的方式 。 当一个对象被一个或一个以上的引用变量所引用时,它处于可达状态,不可能被系统垃圾回收机制回收 。 软引用 (SoftReference)

软引用需要通过 SoftReference 类来实现,当一个对象只有软引用时,它有可能被垃圾回收机制回收。 对于只有软引用的对象而言 ,当系统内存空间足够时,它不会被系统回收,程序也可使用该对象; 当系统内存空间不足时,系统可能会回收它。 软引用通常用于对内存敏感的程序中 。 弱引用 (WeakReference)

弱引用通过 WeakReference 类实现,弱引用和软引用很像,但弱引用的引用级别更低。 对于只有弱引用的对象而言 ,当系统垃圾回收机制运行时,不管系统内存是否足够,总会回收该对象所占用的内存 。 当然,并不是说当一个对象只有弱引用时,它就会立即被回收一一一正如那些失去引用的对象一样,必须等到系统垃圾回收机制运行时才会被回收。 虚引用 (PhantomReference)

虚引用通过 PhantomReference 类实现,虚引用完全类似于没有引用 。 虚引用对对象本身没有太大影响,对象甚至感觉不到虚引用的存在。 如果一个对象只有一个虚引用时,那么它和没有引用的效果大致相同。 虚引用主要用于跟踪对象被垃圾回收的状态,虚引用不能单独使用,虚引用必须和引用队列 ( ReferenceQueue ) 联合使用。 引用队列由 java.lang.ref.ReferenceQueue 类表示,它用于保存被回收后对象的引用。当联合使用软引用、弱引用和引用队列时,系统在回收被引用的对象之后,将把被回收对象对应的引用添加到关联的 引用队列中 。与软引用和弱引用不同的是 ,虚引用在对象被释放之前,将把它对应的虚引用添加到它关 联的引用队列中,这使得可以在对象被回收之前采取行动 。

软引用和弱引用可以单独使用,但虚引用不能单独使用,单独使用虚引用没有太大的意义。虚引用 的主要作用就是跟踪对象被垃圾回收的状态,程序可以通过检查与虚引用关联的引用队列中是否已经包 含了该虚引用,从而了解虚引用所引用的对象是否即将被回收 。

修饰符的适用范围

外部类/接口 成员属性 方法 构造器 初始化块 成员内部类 局部成员 public True True True True True protected True True True True 包访问控制符 True True True True neutral True neutral private True True True True abstract True True True final True True True True True statlc True True True True strictfp True True True synchronized True native True transient True Volatile True default True strictfp 关键宇的含义是 FP-strict ,也就是精确浮点的意思。 在 Java 虚拟机进行浮点运算时 , 如果没有指定 stnct年关键宇, Java 的编译器和运行时环境在浮点运算上不一定令人满意 。一旦使用了 strictf挣 来 修饰类、接口或者方法时,那么在所修饰的范围内 Java 的编译器和运行时环境会完全依照浮点规范 IEEE-754 来执行 。 因此,如果想让浮点运算更加精确,就可以使用 strictfp 关键字来修饰类、接口和方法。

native 关键宇主要用于修饰一个方法,使用 native 修饰的方法类似于一个抽象方法 。 与抽象方法不 同的是, native 方法通常采用 C 语言来实现。如果某个方法需要利用平台相关特性,或者访问系统硬件 等,则可以使用 native 修饰该方法,再把该方法交给 C 去实现 。一旦 Java 程序中包含了 native 方法, 这个程序将失去跨平台的功能 。

[^]:


高内聚低耦合

如下这些接口设计原则,就是参考低耦合高内聚

设计模式一般参照六大设计原则,许多的设计模式,包括一些框架,都是参考高内聚低耦合这个点的。

单一职责原则:一个类值负责一个功能的职责

开闭原则:扩展开放,修改关闭。

里氏代换原则:使用父类的地方都能使用子类对象

依赖倒转原则:针对接口编程,

接口隔离原则:针对不同部分用专门接口,不用总接口,需要哪些接口就用哪些接口

迪米特法则: 软件实体类,尽量不与其他实体类发生关系相互作用,对外都统一的暴露接口就行了 ———————————————— 版权声明:本文为CSDN博主「IT-source」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。 原文链接:什么是高内聚低耦合_ITsource-CSDN博客_高内聚低耦合

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值