java学习记录


JVM加载class文件的原理机制

类的加载分为隐式加载和显示加载

隐式加载:隐式加载指的是程序在使用new等方式创建对象时,会隐式的调用类的加载器把对应的类加载到JVM中

显示加载:显示加载指的是通过直接调用class.forName()方法来把所需要的类加载到JVM中

Java类的加载是动态的,它并不会一次性将所有类全部加载后再运行,而是保证程序运行的基础类(像是基类)完全加载到JVM中,至于其他类,则在需要的时候才加载。这当然就是为了节省内存开销。

Java的类加载器有三个,对应Java的三种类:系统类、扩展类和自定义类

Bootstrap Loader // 负责加载系统类 (指的是内置类,像是String,对应于C#中的System类和C/C++标准库中的类)

ExtClassLoader // 负责加载扩展类(就是继承类和实现类)

AppClassLoader // 负责加载应用类(程序员自定义的类)
类加载的主要步骤分为:

  1. 装载。根据查找路径找到相对应的class文件,然后导入
  2. 链接。链接又可以分为3步
    ①检查。检查待加载的class文件的正确性。
    ②准备。给类中的静态变量分配存储空间。
    ③解析。将符号引用转换成直接引用.
  3. 初始化。对静态变量和静态代码块执行初始化工作

    垃圾回收(GC)

    主要作用:回收程序中不在使用的内存
    垃圾回收期来自动检测对象的作用域,可自动的把不在被使用的存储空fang间释放掉。
    垃圾回收器负责完成3项任务:分配内存、确保被引用对象的内存不被错误的回收以及回收不再被引用的对象的内存空间。
    垃圾回收器优点: 提高了开发人员的生产效率,保证了程序的稳定性.
    垃圾回收器缺点:垃圾回收器必须跟踪内存的使用情况,这必会增加JVM负担,降低程序的执行效率。

    判断对象存活算法

    1、 引用计数器算法:
    引用计数器算法是给每个对象设置一个计数器,当有地方引用这个对象的时候,计数器+1,当引用失效的时候,计数器-1,当计数器为0的时候,JVM就认为对象不再被使用,是“垃圾”了。
    引用计数器实现简单,效率高;但是不能解决循环引用问问题(A对象引用B对象,B对象又引用A对象,但是A,B对象已不被任何其他对象引用),同时每次计数器的增加和减少都带来了很多额外的开销,所以在JDK1.1之后,这个算法已经不再使用了。
    2、 根搜索方法:
    根搜索方法是通过一些“GCRoots”对象作为起点,从这些节点开始往下搜索,搜索通过的路径成为引用链(ReferenceChain),当一个对象没有被GCRoots的引用链连接的时候,说明这个对象是不可用的。
    GCRoots对象包括:
    虚拟机栈(栈帧中的本地变量表)中的引用的对象。
    方法区域中的类静态属性引用的对象。
    方法区域中常量引用的对象。
    本地方法栈中JNI(Native方法)的引用的对象。

    垃圾回收算法

1、标记-清除算法
标记-清除(Mark-Sweep)算法是现代垃圾回收算法的思想基础。标记-清除算法将垃圾回收分为两个阶段:标记阶段和清除阶段。一种可行的实现是,在标记阶段,首先通过根节点,标记所有从根节点开始的可达对象。因此,未被标记的对象就是未被引用的垃圾对象(好多资料说标记出要回收的对象,其实明白大概意思就可以了)。然后,在清除阶段,清除所有未被标记的对象。

2、 标记整理算法
标记整理算法类似与标记清除算法,不过它标记完对象后,不是直接对可回收对象进行清理,而是让所有存活的对象都向一端移动,然后直接清理掉边界以外的内存。
缺点:
1、效率问题,(同标记清除算法)标记和整理两个过程的效率都不高;
优点:
1、相对标记清除算法,解决了内存碎片问题。
2、没有内存碎片后,对象创建内存分配也更快速了(可以使用TLAB进行分配)。

3、 复制算法
复制算法可以解决效率问题,它将可用内存按容量划分为大小相等的两块,每次只使用其中的一块,当这一块内存用完了,就将还存活着的对象复制到另一块上面,然后再把已经使用过的内存空间一次清理掉,这样使得每次都是对整个半区进行内存回收,内存分配时也就不用考虑内存碎片等复杂情况,只要移动堆顶指针,按顺序分配内存即可(还可使用TLAB进行高效分配内存)。

4、 分代收集算法
当前商业虚拟机都是采用分代收集算法,它根据对象存活周期的不同将内存划分为几块,一般是把Java堆分为新生代和老年代,然后根据各个年代的特点采用最适当的收集算法,在新生代中,每次垃圾收集都发现有大批对象死去,只有少量存活,就选用复制算法,而老年代因为对象存活率高,没有额外空间对它进行分配担保,就必须使用“标记清理”或者“标记整理”算法来进行回收。

内存泄漏

内存泄漏是指一个不再被程序使用的对象或变量还在内存中占有存储空间  

内存泄漏的两种情况:一种是在堆中申请的空间没有被释放;二是对象已不在被使用,但还任然在内存中保留着。而垃圾回收机制的引入可以有效的解决第一种情况;对第二种情况而言垃圾回收机制则无法保证不再使用的对象会被释放。(java语言主要指的是第二种情况)
引起内存泄漏主要有以下几个方面:
1)静态集合类,例如HashMap和Vector。如果这些容器为静态的,由于它们的生命周期与程序一致,那么容器中的对象在程序结束之前将不能被释放从而造成内存泄漏。
2)各种连接,例如数据库连接、网络连接以及IO连接等。在对数据库进行操作的过程中,首先需要建立与数据库的连接,当不再使用时,需要调用close方法来释放与数据库连接。只有连接被关闭后,垃圾回收器才会回收对应的对象。否则,如果在访问数据库的过程中,对Connection、Statement或ResultSet不显示的关闭,将会造成大量的对象无法被回收,从而引起内存泄漏。
3)监听器。在java语言中,往往会使用到监听器。通常一个应用中会用到多个监听器,但在释放对象的同时往往没有相应的删除监听器,这也可能导致内存泄漏。
4)变量不合理的作用域。一般而言,如果一个变量定义的作用范围大于其使用范围,很有可能会造成内存泄漏,另一方面如果没有及时的吧对象设置为null,很有可能会导致内存泄漏的发生。
5)单例模式可能会造成内存泄漏。

Class BigClass{}
  Class Singleton{
    private BigClass bc;
    private static Singleton instance Singleton(new BigClass);
    private Simgleton(BigClass bc){this.bc=bc}
    public Singleton getInstance(){
      return instance;
    }
  }     

Singleton存在一个对对象BigClass的引用,单例对象以静态变量的方式存储,因此在JVM的整个生命周期都存在,同时有一个对象BigClass的引用,导致BigClass不能被回收。

以发生的方式来分类,内存泄漏可以分为4类:

  1. 常发性内存泄漏。发生内存泄漏的代码会被多次执行到,每次被执行的时候都会导致一块内存泄漏。
  2. 偶发性内存泄漏。发生内存泄漏的代码只有在某些特定环境或操作过程下才会发生。常发性和偶发性是相对的。对于特定的环境,偶发性的也许就变成了常发性的。所以测试环境和测试方法对检测内存泄漏至关重要。
  3. 一次性内存泄漏。发生内存泄漏的代码只会被执行一次,或者由于算法上的缺陷,导致总会有一块仅且一块内存发生泄漏。比如,在类的构造函数中分配内存,在析构函数中却没有释放该内存,所以内存泄漏只会发生一次。
  4. 隐式内存泄漏。程序在运行过程中不停的分配内存,但是直到结束的时候才释放内存。严格的说这里并没有发生内存泄漏,因为最终程序释放了所有申请的内存。但是对于一个服务器程序,需要运行几天,几周甚至几个月,不及时释放内存也可能导致最终耗尽系统的所有内存。所以,我们称这类内存泄漏为隐式内存泄漏。

内存溢出

指程序在申请内存时,没有足够的内存空间供其使用,出现out of memory;比如申请了一个integer,但给它存了long才能存下的数,那就是内存溢出。

修饰符

Public:当前项目任何包中都可使用
Private:只能在当前类中使用
Protected:在当前包的所有类中使用以及不同包的子类中使用
默认修饰符default:在当前包的所有类中使用

Java程序初始化顺序

父类静态变量 ——> 父类静态代码块 ——> 子类静态变量 ——> 子类静态代码块 ——> 父类非静态变量 ——> 父类非静态代码块 ——> 父类构造函数 ——> 子类非静态变量 ——> 子类非静态代码块 ——> 子类构造函数

Java中数据传递的方式

基本传值 : 不改变原数据的值
引用传值 : 改变原数据的值
基本数据类型传递是该数据的本身对它的修改,不会被保留。
引用数据类型传递也是这个变量值的本身,即对象的引用,而非对象本身,其值的修改将会被保留

位运算

右移(>>)

正数右移高位补0
无符号右移无论正负用0进行补位
负数右移高位补1

左移(<<)

表示左移 左移n位表示原来的值乘2的n次方
正数或者负数左移,低位都是用0补
若超出该类型最大位数,那么编译器会对位移的位数取模 int型32位 取模 33%32 实际移动一位

位与(&)

第一个操作数的第n位于第二个操作数的第n位如果都是1,那么结果的第n为也为1,否则为0 (结果只有1和0)

位或( | )

第一个操作数的第n位于第二个操作数的第n位 只要有一个是1,那么结果的第n为也为1,否则为0

位异或( ^ )

第一个操作数的的第n位于第二个操作数的第n位相反,那么结果的第n为也为1,否则为0

位非( ~ )

操作数的第n位为1,那么结果的第n位为0,反之。

Math类

Round方法:表示四舍五入
Ceil方法:表示向上取整
Floor方法:表示向下取整

String、StringBuffer、StringBuilder、StringTockenizer

String
是不可变类,可以用构造函数初始化和赋值初始化

StringBuffer
是可变类,当对象被创建后任然可以对其值进行修改,只能构造函数初始化
append(基本数据类型 x) 添加字符串
public stringBuffer reverse() 对字符串进行反转
stringBuffer delete(int left,int right) 左闭右开的删除
public char charAt(int n) 取指定位置的字符
public void setCharAt(int n,char c)将指定位置的n改成c
stringBuffer replace(int n,int m,String str)从n-m改成str
public String substring(int start,int and)
public int indexOf(string str)返回str在字符串首次查询的位置
public int length()

StringBuilder
与stringBuffer 类似,都是字符串缓冲区,但线程是不安全的,效率较StringBuffer高

stringTockenizer
用来分割字符串的工具类

多态

方法重载(overload)

方法名相同,而参数不同,返回类型也可以不同,但每个重载的方法都必须有一个独一无二的参数类型列表

方法覆盖(override)

在子类中重写父类的方法,方法名称相同,参数必须相同,返回值类型相同或其子类类型,修饰符不能小于父类范围

Static

一般作用在:成员变量,成员方法(static方法中不能使用this和super关键字,以及实现单例模式),代码块(用来初始化静态变量),内部类(只有内部类才能被定义为ststic)
作用 : 为某特定数据类型或对象分配单一的存储空间,实现某个方法或属性与类而不是对象关联

final关键字

修饰类:表示类为最终的类,也就是不能被继承
修饰方法:表示方法为最终的,改方法不能被重写
修饰属性:表示该属性为常量,需在声明式完成赋值,且其后值不允许被修改

不可变类

可变类与不可变类的区别

不可变类是指当创建了这个类的实例后,就不允许修改它的属性值。在JDK的基本类库中,所有基本类型的包装类,如String、Integer和Long类,都是不可变类,
不可变类:当你获得这个类的一个实例引用时,你不可以改变这个实例的内容。不可变类的实例一但创建,其内在成员变量的值就不能被修改。

如何创建一个不可变类?

  1. 所有成员都是private
  2. 不提供对成员的改变方法,例如:setXXXX
  3. 确保所有的方法不会被重载。手段有两种:使用final Class(强不可变类),或者将所有类方法加上final(弱不可变类)。
  4. 如果某一个类成员不是原始变量(primitive)或者不可变类,必须通过在成员初始化(in)或者get方法(out)时通过深度clone方法,来确保类的不可变。
  5. 可使用覆盖object类的equals()方法和hashcode()方法。在equals方法中,根据对象的属性值来比较两个对象是否相等,并且保证用equals()方法判断为相等的两个对象的hashcode()方法的返回值也相等,这可以保证这些对象能被正确地放到hashmap或hashset集合中。

Java中的引用

1)强引用
  指创建一个对象并把这个对象赋给一个引用变量。强引用有引用变量指向时永远不会被垃圾回收。即使内存不足的时候。像上述Map的小例子中,cloneMapShallow方法的实现就是强引用。要注意的是,在做强引用的时候,用完最好记得解除这种引用关系(置为null)。
2)软引用  
软引用通过SoftReference类来实现。软引用的对象当系统内存充足时和强引用没有太多区别,但内存不足时会回收软引用的对象。

public static void softReference() {
        //创建软引用数组
        SoftReference<TestReference>[] p = new SoftReference[100];
        //赋值
        for (int i = 0; i < p.length; i++) {
            p[i] = new SoftReference<TestReference>(new TestReference("name:" + i));
        }
        //测试
        System.out.println(p[1].get().name);
        System.out.println(p[4].get().name);
        //通知系统进行回收
        System.gc();
        System.runFinalization();
        System.out.println("---------------");
        System.out.println(p[1].get().name);
        System.out.println(p[4].get().name);
    }  

3)弱引用
弱引用通过weakReference类来实现。弱引用与软引用的区别在于:只具有弱引用的对象拥有更短暂的生命周期。在垃圾回收器线程扫描它 所管辖的内存区域的过程中,一旦发现了只具有弱引用的对象,不管当前内存空间足够与否,都会回收它的内存。不过,由于垃圾回收器是一个优先级很低的线程, 因此不一定会很快发现那些只具有弱引用的对象。

public static void weakReference() {
        String str = new String("Test Java");
        // 创建一个如引用对象 指向 str对象
        WeakReference<String> wr = new WeakReference<String>(str);
        str = null;
        // 输出
        System.out.println(wr.get());
        // 强制垃圾回收
        System.gc();
        System.out.println(wr.get());
    }

4)虚引用
  软引用和弱引用可以单独使用,虚引用不能单独使用,需引用的作用是就跟踪对象被垃圾回收的状态,程序可以通过检测与虚引用关联的虚引用队列是否已经包含了指定的虚引用,从而了解虚引用的对象是否即将被回收。
  虚引用通过PhantomRefence类实现,它本身对对象没有影响,类似与没有应用,对象甚至感觉不到虚引用的存在,如果一个对象只有一个虚引用存在,那么他就类似没有应用存在。

public static void phantomReference() {
        // 创建一个对象
        String str = new String("Test Java");
        // 创建一个引用队列
        ReferenceQueue<String> rq = new ReferenceQueue<String>();
        // 创建一个虚引用,指定引用对象.不能单独使用必须关联引用队列
        PhantomReference pr = new PhantomReference(str, rq);
        // 切断强引用
        str = null;
        // 试图取得虚引用对象
        System.out.println(pr.get());
        // 垃圾回收
        System.gc();
        System.runFinalization();
        // 取出引队列中的最先进入队列的引用与pr进行比较
        System.out.println(rq.poll() == pr);
    }

Java内部类

分四种:成员内部类、局部内部类、静态内部类和匿名内部类。

  1. 成员内部类: 即作为外部类的一个成员存在,与外部类的属性、方法并列。
    注意:成员内部类中不能定义静态变量,但可以访问外部类的所有成员。
    a. 内部类中不允许定义静态变量
    b. 内部类中外部类的实例变量可以共存
    c. 外部类的变量如果和内部类的变量没有同名的,则可以直接用变量名访问外部类的变量
    d. 在内部类中访问内部类自己的变量直接用变量名
    e. 也可以在内部类中用”this.变量名”来访问内部类变量
    f. 访问外部类中与内部类同名的实例变量可用”外部类名.this.变量名”。
    g. 外部类的变量如果和内部类的变量没有同名的,则可以直接用变量名访问外部类的变量
    h. 外部类的静态方法访问成员内部类,与在外部类外部访问成员内部类一样.
    成员内部类的优点:
    ⑴ 内部类作为外部类的成员,可以访问外部类的私有成员或属性。(即使将外部类声明为PRIVATE,但是对于处于其内部的内部类还是可见的。
    ⑵ 用内部类定义在外部类中不可访问的属性。这样就在外部类中实现了比外部类的private还要小的访问权限。
    注意:内部类是一个编译时的概念,一旦编译成功,就会成为完全不同的两类。对于一个名为outer的外部类和其内部定义的名为inner的内部类。编译完成后出现outer.class和outer$inner.class两类。

  2. 局部内部类: 即在方法中定义的内部类,与局部变量类似,在局部内部类前不加修饰符public或private,其范围为定义它的代码块。
    注意:局部内部类中不可定义静态变量,可以访问外部类的局部变量(即方法内的变量),但是变量必须是final的。
    a. 可以定义与外部类同名的变量如果内部类没有与外部类同名的变量,在内部类中可以直接访问外部类的实例变量
    b. 可以访问外部类的局部变量(即方法内的变量),但是变量必须是final的
    c. 如果内部类中有与外部类同名的变量,直接用变量名访问的是内部类的变量
    d. 用”this.变量名” 访问的也是内部类变量
    e. 用外部”外部类类名.this.变量名” 访问的是外部类变量
    f. 访问局部内部类必须先有外部类对象
    注意:在类外不可直接生成局部内部类(保证局部内部类对外是不可见的)。要想使用局部内部类时需要生成对象,对象调用方法,在方法中才能调用其局部内部类。通过内部类和接口达到一个强制的弱耦合,用局部内部类来实现接口,并在方法中返回接口类型,使局部内部类不可见,屏蔽实现类的可见性。

  3. 静态内部类: 静态内部类定义在类中,任何方法外,用static定义。
    注意:静态内部类中可以定义静态或者非静态的成员
    静态内部类可以用public,protected,private修饰
    静态内部类中可以定义静态或者非静态的成员
    静态内部类只能访问外部类的静态成员
    静态内部类不能访问外部类的非静态成员
    包括非静态变量和非静态方法
    外部类访问内部类的静态成员:内部类.静态成员
    外部类访问内部类的非静态成员:实例化内部类即可
    注意:生成(new)一个静态内部类不需要外部类成员:这是静态内部类和成员内部类的区别。静态内部类的对象可以直接生成,而不需要通过生成外部类对象来生成。这样实际上使静态内部类成为了一个顶级类。静态内部类不可用private来进行定义。
    注意:当类与接口(或者是接口与接口)发生方法命名冲突的时候,此时必须使用内部类来实现。用接口不能完全地实现多继承,用接口配合内部类才能实现真正的多继承。

  4. 匿名内部类:匿名内部类是一种特殊的局部内部类,它是通过匿名类实现接口。
    匿名内部类的特点:
    1、一个类用于继承其他类或是实现接口,并不需要增加额外的方法,只是对继承方法的事先或是覆盖。
    2、只是为了获得一个对象实例,不需要知道其实际类型。
    3、类名没有意义,也就是不需要使用到。
    静态内部类可以用public,protected,private修饰
    静态内部类中可以定义静态或者非静态的成员
    静态内部类只能访问外部类的静态成员,包括静态变量和静态方法
    静态内部类不能访问外部类的非静态成员,包括非静态变量和非静态方法
    外部类访问内部类的静态成员:内部类.静态成员
    外部类访问内部类的非静态成员:实例化内部类即可
    注:一个匿名内部类一定是在new的后面,用其隐含实现一个接口或实现一个类,没有类名,根据多态,我们使用其父类名。因他是局部内部类,那么局部内部类的所有限制都对其生效。匿名内部类是唯一一种无构造方法类。大部分匿名内部类是用于接口回调用的。匿名内部类在编译的时候由系统自动起名Out$1.class。如果一个对象编译时的类型是接口,那么其运行的类型为实现这个接口的类。因匿名内部类无构造方法,所以其使用范围非常的有限。当需要多个对象时使用局部内部类,因此局部内部类的应用相对比较多。匿名内部类中不能定义构造方法。如果一个对象编译时的类型是接口,那么其运行的类型为实现这个接口的类。

内部类总结:
1.首先,把内部类作为外部类的一个特殊的成员来看待,因此它有类成员的封闭等级:private ,protected,默认(friendly),public
它有类成员的修饰符: static,final,abstract
2.非静态内部类nested inner class,内部类隐含有一个外部类的指针this,因此,它可以访问外部类的一切资源(当然包括private)
外部类访问内部类的成员,先要取得内部类的对象,并且取决于内部类成员的封装等级。
非静态内部类不能包含任何static成员.
3.静态内部类:static inner class,不再包含外部类的this指针,并且在外部类装载时初始化.
静态内部类能包含static或非static成员.
静态内部类只能访问外部类static成员.
外部类访问静态内部类的成员,循一般类法规。对于static成员,用类名.成员即可访问,对于非static成员,只能用对象.成员进行访问
4.对于方法中的内部类或块中内部类只能访问块中或方法中的final变量。

类成员有两种static , non-static,同样内部类也有这两种
non-static 内部类的实例,必须在外部类的方法中创建或通过外部类的实例来创建(OuterClassInstanceName.new innerClassName(ConstructorParameter)),并且可直接访问外部类的信息,外部类对象可通过OuterClassName.this来引用
static 内部类的实例, 直接创建即可,没有对外部类实例的引用。
内部类不管static还是non-static都有对外部类的引用
non-static 内部类不允许有static成员

方法中的内部类只允许访问方法中的final局部变量和方法的final参数列表,所以说方法中的内部类和内部类没什麽区别。但方法中的内部类不能在方法以外访问,方法中不可以有static内部类
匿名内部类如果继承自接口,必须实现指定接口的方法,且无参数
匿名内部类如果继承自类,参数必须按父类的构造函数的参数传递

Instanceof(对象判断)

instanceof的用处:判断左边对象是否为右边类的实例;
只能用作对象的判断。
返回值类型为:boolean
使用:boolean result=female instaceof Person; //female为对象;Person为类

Assert(断言)

断言是一个软件调试的方法,提供 一种在代码中进行正确性检查的机制,通常在主程序开发个测试时使用。(为了提高程序的效率,软件发布后,assert检查默认是关闭的)
主要作用:检查控制流
检查输入参数是否有效
检查程序不变量

Volatile(类型修饰符)

被设计用来修饰被不同线程访问和修改的变量,被volatile类型定义的变量相当于系统直接从内存中提取,而不会利用缓存
示例:private volati Boolean flag
缺点: 不能保证操作的原子性,并且使用volatile会阻止编译器对代码的优化。

Strictfp(关键字 strict float point的缩写)

指的是精确浮点,用来确保浮点数运算的准确性
当一个类被strictfp修饰时,所有方法都会自动被syrictfp修饰。
示例:public strictfp class Test{}

异常处理

Finally中代码执行时间:由于程序执行return意味着结束,因此任何语句要执行都只能在return前执行(除非碰到exit 函数)
Finally代码块不一定执行:
1>进入try语句块之前出现异常,会直接结束
2>档程序在try块中强制退出时也不会执行finally块
异常处理原理:
异常是指程序运行时(非编译时)所发生的非正常情况或错误,当程序违反了语义规则时,JVM就会将出现的错误表示为一个异常并抛出
目的 :为了提高程序的安全性和鲁棒性

UNICODE编码

  Java开发者必须牢记:在Java中字符仅以一种形式存在,那就是Unicode(不选择任何特定的编码,直接使用他们在字符集中的编号,这是统一的唯一方法)。由于java采用unicode编码,char 在java中占2个字节。2个字节(16位)来表示一个字符。
  这里的Java中是指在JVM中、在内存中、在代码里声明的每一个char、String类型的变量中。
  JVM的折中约定使得一个字符分为两部分:JVM内部和OS的文件系统。在JVM内部,统一使用Unicode表示,当这个字符被从JVM内部移到外部(即保存为文件系统中的一个文件的内容时),就进行了编码转换,使用了具体的编码方案。因此可以说所有的编码转换只发生在边界的地方,JVM和OS的交界处,也就是各种输入/输出流(或者Reader,Writer类)起作用的地方。

Cloneable接口(标记接口)

要想一个类可以被clone,必须满足两点,
第一,它必须实现了Cloneable接口,否则会抛出CloneNotSupportedException异常;
第二,它必须提供一个public的clone方法,也就是重写Object.clone()方法,否则编译不能通过。
第三,对于存在可变域的类,在clone方法中需要对这些可变域进行拷贝(即深拷贝)。

实现cloneable接口的类标记为可克隆的而且它的对象可以使用在object类中定义的clone()方法克隆,当类只有一些基本的数据类型时就采用
但当类中包含了一些对象时,就需要用到深复制
深复制两种方案:
1、序列化(serialization)这个对象,再反序列化回来,就可以得到这个新的对象,无非就是序列化的规则需要我们自己来写
2、继续利用 clone() 方法,既然 clone() 方法,是我们来重写的,实际上我们可以对其内的引用类型的变量,再进行一次 clone()

组合和继承区别

: 组 合 关 系 : : 继 承 关 系 :
: 优点:不破坏封装,整体类与局部类之间松耦合,彼此相对独立 : : 缺点:破坏封装,子类与父类之间紧密耦合,子类依赖于父类的实现,子类缺乏独立性:
: 优点:具有较好的可扩展性 : : 缺点:支持扩展,但是往往以增加系统结构的复杂度为代价:
: 优点:支持动态组合。在运行时,整体对象可以选择不同类型的局部对象 : : 缺点:不支持动态继承。在运行时,子类无法选择不同的父类:
: 优点:整体类可以对局部类进行包装,封装局部类的接口,提供新的接口 : : 缺点:子类不能改变父类的接口:
: 缺点:整体类不能自动获得和局部类同样的接口 : : 优点:子类能自动继承父类的接口:
: 缺点:创建整体类的对象时,需要创建所有局部类的对象 : : 优点:创建子类的对象时,无须创建父类的对象:

抽象类

意义java中父类实例化无实际意义,因此父类无需实例化
1、抽象类不能被实例化
2、抽象类中包含属性、方法、构造方法、抽象方法
3、抽象方法必须没有方法体
4、子类必须重写父类抽象方法
5、如果一个类中包含抽象方法,则改类必须是抽象类

接口

(interface)是与类并行的概念
接口可以看做是一个特殊的抽象类,常量与抽象方法集合
成员的作用域修饰符都是public
常量:所有的常量用public static final 修饰
Public static final int i=10;
Public static final boolean flag=flag=false; //绿色代码部分可省略

抽象方法:所有抽象方法用public abstract修饰
Public abstract void method(); //注意:抽象方法没有方法体
接口没有构造器
类可以实现多个接口
实现接口需重写接口的所有方法
案例
Interface AA{}
class bb implements AA{重写接口的所有方法}
abstract class cc implement AA{}

实现接口匿名类对象
A b=new A(){重写接口的所有方法};

总结:
1、接口不能被实例化,即不包含构造方法
2、接口中可以包含抽象方法,接口中的抽象方法默认为public abstarct
3、接口中可包含公有的静态的常量值,默认为public static final
4、接口中在jdk8.0以后存在默认方法和静态方法
默认方法:
Public default void print(){}
静态方法
Public static void show(){}


文章作者: Hanair
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 Hanair !
 本篇
java学习记录 java学习记录
JVM加载class文件的原理机制类的加载分为隐式加载和显示加载 隐式加载:隐式加载指的是程序在使用new等方式创建对象时,会隐式的调用类的加载器把对应的类加载到JVM中 显示加载:显示加载指的是通过直接调用class.forName()
2020-04-21 Hanair
下一篇 
mysql启动失败 mysql启动失败
mysql启动失败:mysql服务无法启动 mysql5.7安装 mysql发生系统错误2 解决方法:切换到bin目录后,首先删除前面安装的MySQL服务,然后在重新安装MySQL服务,然后启动。 document.que
2020-04-08 Hanair
  目录