疯狂Java讲义 读书笔记(一)

李刚老师的《疯狂Java讲义》(第三版)是我的启蒙作品,前前后后一共看了3遍,每次都有新的认识。

接下来的一段时间就将一些经典的知识记录下来。

1.计算机高级语言的执行方式分为编译型和解释型,前者运行高效但不能跨平台(C,C++,Object-C),后台可以跨平台但效率不高(Ruby,Python)。Java比较特殊,先编译生成.class,再在JVM中解释。

2.垃圾回收机制:依靠垃圾回收算法,何时回收对Java程序员而言具有透明性,因此要养成良好的习惯——对于不需要的对象,不要引用他们。(在堆中进行回收)

3.Java是强类型语言:所有类变量必须先声明后使用,指定类型的变量只能接受与类型相匹配的值。

4.Java支持两种类型:

  1. 基本类型:布尔类型、数值类型
  2. 引用类型:类、接口、数组。

5.强制类型转化:造成溢出时,之前一直觉得19.745会变成19(实在汗颜),其实是转换成二进制后再进行截取。

6.常量池:在编译期被确定,并已被保存在.class文件中的一些数据。包括类、方法、接口中的常量,也包括字符串常量。

7.switch语句:控制表达式的数据类型只能是byte,short,char,int,枚举,String.

8.break和continue可以通过标签跳到指定的循环层

public class BreakTest2
{
    public static void main(String[] args)
    {
        outer:
        for (int i = 0 ; i < 5 ; i++ )
        {
            for (int j = 0; j < 3 ; j++ )
            {
                System.out.println("i的值为:" + i + "  j的值为:" + j);
                if (j == 1)
                {
                    break outer;
                }
            }
        }
    }
}

9.栈和堆:

  1. 当一个方法执行时,每个方法都会建立自己的内存栈,在这个方法内定义的变量将会逐个放入这块栈内存里,随着方法的执行结束,这个方法的内存栈也将自动销毁。因此,所有在方法中定义的局部变量都是放在栈内存中;
  2. 当我们在程序中创建一个对象是,这个对象将被保存到运行是数据区中,以便反复利用,这个运行是数据区就是堆内存。堆内存中的对象不会因方法结束而销毁,就算方法结束后,这个对象还可能被另一个变量所引用,则这个对象不会销毁,只有当一个变量没有任何引用变量引用它,那么系统的垃圾回收器才会在合适的时候回收它;

10.Arrays:Java8增加的工具类,在java.util包下,支持并发编程

        // 定义一个a数组
        int[] a = new int[]{3, 4 , 5, 6};
        // 定义一个a2数组
        int[] a2 = new int[]{3, 4 , 5, 6};
        // a数组和a2数组的长度相等,每个元素依次相等,将输出true
        System.out.println("a数组和a2数组是否相等:"
            + Arrays.equals(a , a2));
        // 通过复制a数组,生成一个新的b数组
        int[] b = Arrays.copyOf(a, 6);
        System.out.println("a数组和b数组是否相等:"
            + Arrays.equals(a , b));
        // 输出b数组的元素,将输出[3, 4, 5, 6, 0, 0]
        System.out.println("b数组的元素为:"
            + Arrays.toString(b));
        // 将b数组的第3个元素(包括)到第5个元素(不包括)赋为1
        Arrays.fill(b , 2, 4 , 1);
        // 输出b数组的元素,将输出[3, 4, 1, 1, 0, 0]
        System.out.println("b数组的元素为:"
            + Arrays.toString(b));
        // 对b数组进行排序
        Arrays.sort(b);
        // 输出b数组的元素,将输出[0, 0, 1, 1, 3, 4]
        System.out.println("b数组的元素为:"
            + Arrays.toString(b));
    }

11.Java语言通过new关键字调用构造器。

12.this关键字:

  1. 让类中的一个方法,访问该类里的另一个方法或者实例变量。
  2. this所代表的对象是不确定的,但他的类是确定的。
  3. 在构造器中,this代表该构造器正在初始化的对象。
  4. 如果在某个方法中把this作为返回值,则可以多次连续调用同一个方法。
public class ReturnThis
{
    public int age;
    public ReturnThis grow()
    {
        age++;
        // return this返回调用该方法的对象
        return this;
    }
    public static void main(String[] args)
    {
        ReturnThis rt = new ReturnThis();
        // 可以连续调用同一个方法
        rt.grow()
            .grow()
            .grow();
        System.out.println("rt的age成员变量值是:" + rt.age);
    }
}

13.static:不要使用对象去调用static修饰的成员变量,要使用类去调用。

14.值传递:将实际参数的副本传入方法内,参数本身不会受到任何影响。

15.形参可变的方法:定义方法时,在最后一个形参的类型后增加三个点(...)。多个参数值被当成数组传入。与传入数组相比,形参可变更加简洁,但一个方法只能有一个可变形参。

public class Varargs
{
    // 定义了形参个数可变的方法
    public static void test(int a , String... books)
    {
        // books被当成数组处理
        for (String tmp : books)
        {
            System.out.println(tmp);
        }
        // 输出整数变量a的值
        System.out.println(a);
    }
    public static void main(String[] args)
    {
        // 调用test方法
        test(5 , "疯狂Java讲义" , "轻量级Java EE企业应用实战");
    }
}

16.递归算法:一个方法调用自身,一定要向已知方向递归。

17.局部变量:形参(方法签名中定义的变量),方法局部变量(在方法内定义),代码块局部变量(在代码块内定义),局部变量不属于任何类或者实例,它总是保存在其所在方法的栈内存中。

18.封装:类的成员变量不直接暴露,而是通过方法实现操作和访问,以便于在方法中添加一些限制条件

19.高内聚:尽可能把模块的内部数据、功能实现细节隐藏在模块内部独立完成,不允许外部直接干预;低耦合:仅暴露少量的方法给外部使用。

20.静态导入:JDK1.5增加的导包方法,用于导入包内的静态成员 import static package...

21.构造器:

  1. 构造器是创建Java对象的重要途径,但这个对象并不是完全由构造器负责创建的。
  2. 子类的构造器必定会调用其父类的构造器(没有super和this时默认调用无参的构造器)。

22.方法重写(覆盖)原则:

  1. 两同:方法名、参数列表相同
  2. 两小:子类方法返回值<=父类方法返回值
  3. 一大:子类方法访问权限>=父类方法访问权限

23.当程序创建一个子类对象时,系统不仅会为该类中定义的实例变量分配内存,也会为它从父类继承得到的所有实例变量分配内存。

24.引用变量类型:

  1. 编译时类型:由声明该变量时使用的类型决定
  2. 运行时类型:由实际赋给该变量的对象决定
  3. 编译时类型和运行时类型不同时,就出现了所谓的多态
  4. 引用变量在编译阶段只能调用其编译时类型所具有的方法,但运行时则执行它运行时类型所具有的方法(可用强制类型转换解决问题)。

25.向上转型:把一个子类对象直接赋值给父类引用变量;强制类型转换:把一个父类对象赋给子类引用变量

26多态:

  1. 相同类型的变量,调用同一个方法时呈现出多种不同的行为特征叫做多态
  2. 成员变量不存在多态,总是调用父类的值

27.instanceof:判断前面的对象是否是后面的类,或者其子类、实现类的实例

String str="str";
//true
System.out.println(str instanceof Object);
Object obj=new Object();
//false
System.out.println(obj instanceof String);

28.继承与组合:

  1. 继承代表"is a",组合代表"has a"
  2. 创建子类对象,系统会为其父类所定义的实例变量分配内存空间,因此继承和组合在系统开销上没有太大的区别

29.初始化块:

  1. 初始化块在构造器之前执行(编译后初始化块的内容会还原到构造器中)
  2. 静态初始化块在普通初始化块之前执行

30.包装类:包装类的实例可以与数值类型直接比较

31.toString:自定义类时,尽量重写类的toString方法,便于输出实例的值

32.==与equals:

  1. 对于引用类型,只有二者指向同一个对象,==才会等于true 
  2. String类型情况如下,编译时确定的数据在常量池中,运行时生成的数据在堆内存中
    // s1直接引用常量池中的"疯狂Java"
        String s1 = "疯狂Java";
        String s2 = "疯狂";
        String s3 = "Java";
        // s4后面的字符串值可以在编译时就确定下来
        // s4直接引用常量池中的"疯狂Java"
        String s4 = "疯狂" + "Java";
        // s5后面的字符串值可以在编译时就确定下来
        // s5直接引用常量池中的"疯狂Java"
        String s5 = "疯" + "狂" + "Java";
        // s6后面的字符串值不能在编译时就确定下来,
        // 不能引用常量池中的字符串
        String s6 = s2 + s3;
        // 使用new调用构造器将会创建一个新的String对象,
        // s7引用堆内存中新创建的String对象
        String s7 = new String("疯狂Java");
        System.out.println(s1 == s4); // 输出true
        System.out.println(s1 == s5); // 输出true
        System.out.println(s1 == s6); // 输出false
        System.out.println(s1 == s7); // 输出false

33.重写equals的条件:

  1. 自反性:x.equals(x)=true
  2. 对称性:若x.equals(y)=true,则y.equals(x)=true
  3. 传递性:若x.equals(y)=true,y.equals(z)=true,则x.equals(z)=true
  4. 一致性:只要x.equals(y)=true且x,y不变,无论调用多少次结果都不变
  5. 对任何不适Null的x,x.equals(null)=false 6.equals相同,则hashcode相同

34.null类型的实例可以访问类的静态方法和静态变量,在底层是通过该实例的类去访问的

35.final:

  1. final变量不是不能被赋值,而是不能被改变
  2. final变量必须由程序员显示的指定初始值
  3. final修饰引用变量时,引用地址不可以改变,对象可以改变
  4. final修饰的方式不能被重写,但是可以被重载

36.宏变量:定义final变量时就为该变量指定了初始值,而且可以在编译时就确定下来,编译器会把程序中所有用到改变量的地方直接替换成该变量的值(进入常量池)

37.不可变类:

  1. private final修饰所有成员变量
  2. 只有getter没有setter

38.缓存池:先进先出缓存实例,重写了equals和hsahcode

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);
    }
}

39.默认方法:

  1. JDK1.8后新增的方法,在接口中一共有三种方法,抽象方法(abstract),类方法(static),默认方法(default),后两者必须有方法实现
  2. 使用接口的实例来调用默认方法

40.抽象类:抽象类作为多个子类的抽象父类,可以被当成系统实现过程的中间产品,这个中间产品已经实现了系统的部分功能,但这个产品依然不能当成最终产品,必须由进一步的完善,

41.内部类:

  1. 提供更好的封装
  2. 内部类成员可以直接访问外部类的私有数据
  3. 匿名内部类适用于创建只需要一次使用的类
  4. 局部内部类和匿名内部类不是类成员
  5. 包含内部类的类被称为外部类

42.非静态内部类:

  1. 非静态内部类对象里保存了一个外部类的引用
  2. 外部类对象访问非静态内部类成员时,可能非静态普通内部类对象根本不存在
  3. 不允许在非静态内部类定义静态成员

43.静态内部类:

  1. 静态内部类可以包含静态成员和非静态成员
  2. 静态内部类是外部类的类相关,静态内部类对象寄生在外部类的类本身中,只持有外部类的类引用,没有外部类的对象引用

44.内部类的使用:

  1. 尽量使用静态内部类(调用简单)
  2. 局部内部类没有什么卵用 
  3. 非静态内部类的构造器必须由其外部类的对象来调用
public class SubClass extends Out.In
{
    //显示定义SubClass的构造器
    public SubClass(Out out)
    {
        //通过传入的Out对象显式调用In的构造器
        out.super("hello");
    }
}

45.匿名内部类:

  1. 必须继承一个父类或者实现一个接口
  2. 不能是抽象类
  3. 不能定义构造器
  4. JDK1.8以前被匿名内部类访问的局部变量必须使用final修饰,1.8以后会自动加上final修饰

46.枚举类:可以使用枚举类来替代静态final常量

public class Enum {
public enum Season{
    spring,summer,fall,winter;
}
public static void main(String[] args) {
    Season season=Season.spring;
    System.out.println(season);
    switch (season){
    case spring:
        System.out.println("spring");
        break;
    case summer:
        System.out.println("summer");
        break;
    case fall:
        System.out.println("fall");
        break;
    case winter:
        System.out.println("winter");
        break;
    }
    
}

47.垃圾回收:

  1. 只负责回收对象,不负责物理资源
  2. 程序无法精确控制垃圾回收的运行
  3. 在回收对象前,会调用对象的finalize方法尝试让其获得新的引用以复活
  4. finalize方法何时被调用具有不确定性
public class FinalizeTest
{
    private static FinalizeTest ft = null;
    public void info()
    {
        System.out.println("测试资源清理的finalize方法");
    }
    public static void main(String[] args) throws Exception
    {
        // 创建FinalizeTest对象立即进入可恢复状态
        new FinalizeTest();
        // 通知系统进行资源回收
//        System.gc();  //// 强制垃圾回收机制调用可恢复对象的finalize()方法
//        Runtime.getRuntime().runFinalization();   //
        System.runFinalization();   //
        ft.info();
    }
    public void finalize()
    {
        // 让tf引用到试图回收的可恢复对象,即可恢复对象重新变成可达
        ft = this;
    }
}

48.引用类型:

  1. 强引用(最常见的引用)
  2. 软引用(当系统内存空间不够时会被回收)
  3. 弱引用(比软引用级别更低,当系统垃圾回收机制运行时,无论内存够不够都会被回收)
  4. 虚引用(没什么用)
原文地址:https://www.cnblogs.com/xx-wqj/p/5990301.html