目录

一、Java基础

1、Java面向对象

2、Java内存解析

3、Java特殊关键字

4、Java数组

5、Java容器

6、Java异常处理

7、Java常用类

8、Java流

9、Java多线程机制

10、Java零散知识点


二、JVM

1、《深入理解Java虚拟机》读书笔记

第2章 Java内存区域与内存溢出异常


三、Java并发编程

1、《Java并发编程实战》读书笔记

第1章 简介

第2章 线程安全性


四、设计模式

1、《Head First设计模式》读书笔记


五、代码优化

1、《Effective Java》读书笔记

第2章 创建和销毁对象

第1章 简介

1.1 并发简史

在计算机中加入操作系统来实现多程序同时运行的原因:

① 提高资源利用率
② 使所有用户和程序对计算机上的资源拥有同等的使用权,提高公平性
③ 可以通过编写多个程序来实现多任务计算,比只编写一个程序来计算所有任务更容易实现,提高便利性

1.2 线程的优势

① 发挥多处理器的强大能力。
② 建模的简单性。
③ 异步事件的简化处理。
④ 响应更灵敏的用户界面。

1.3 线程带来的风险

安全性问题。安全性的含义是“永远不发生糟糕的事情”。在没有充足同步的情况下,多个线程中的操作执行顺序是不可预测的,甚至会产生奇怪的结果。
活跃性问题。活跃性的含义是“某件正确的事情最终会发生”。串行程序中活跃性问题的形式之一为无限循环从而导致循环之后的代码无法执行,线程中的活跃性问题例如线程B始终不释放资源从而导致线程A一直在等待而不继续运行。
性能问题。性能问题包括服务时间过长、响应不灵敏、吞吐率过低、资源消耗过高、可伸缩性较低等。线程能够提升程序的性能,但无论如何都会带来某种程度的运行时开销。

第2章 Java内存区域与内存溢出异常

本章从概念上介绍Java虚拟机内存的各个区域,讲解这些区域的作用、服务对象以及其中可能产生的问题。

一、运行时数据区域

1、程序计数器

程序计数器是一块较小的内存空间,可以看作是当前线程所执行的字节码的行号指示器。在虚拟机的概念模型里,字节码解释器工作时就是通过改变这个计数器的值来选取下一条需要执行的字节码指令,分支、循环、跳转、异常处理、线程恢复等基础功能都需要依赖这个计数器来完成。

Java虚拟机的多线程是通过线程轮流切换并分配处理器执行时间的方式来实现的,在任何一个确定的时刻,一个处理器(对于多核处理器来说是一个内核)都只会执行一条线程中的指令。因此,为了线程切换后能恢复到正确的执行位置,每条线程都需要有一个独立的程序计数器,各条线程之间计数器互不影响,独立存储,我们称这类内存区域为“线程私有”的内存。

继续阅读“第2章 Java内存区域与内存溢出异常”

Java零散知识点

1、Java是值传递的

① 对于基本数据类型short、int、long、float、double、char、byte、boolean这八种按值传递调用函数并不会改变在原函数中的值。

② 对于引用数据类型数组、类、接口按值传递的时候都是传递对象的地址,方法体内改变形参的引用,不会改变实参的引用,但有可能改变实参对象的属性值。

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

        //demo1
        String str = "hello";
        char[] chs = {'w','o','r','l','d'};
        change(str, chs);
        System.out.println(str + " " + new String(chs));
        //输出“hello World”

        //demo2
        StringBuffer sb = new StringBuffer("hello");
        change(sb);
        System.out.println(sb);
        //输出“hello world”
    }

    public static void change(StringBuffer sb) {
        sb.append(" world");
        //此处的sb与main方法中的sb指向同一个对象,改变了对象的内容 
    }

    public static void change(String str, char[] chs) {
        str.replace('h', 'H');
        //因为String的不可变性,此处str指向对象的内容没有发生变化
        //str = str.replace('h', 'H');
        //此处str指向内容为“Hello”的对象,不改变main方法中str的指向

        chs[0] = 'W';
        //此处的chs与main方法中的chs指向同一个对象,改变了对象的内容
    }
}
继续阅读“Java零散知识点”

第2章 创建和销毁对象

本章的主题是创建和销毁对象:何时以及如何创建对象,何时以及如何避免创建对象,如何确保它们能够适时地销毁,以及如何管理对象销毁之前必须进行的各种清理动作。


第一条:用静态工厂方法代替构造器

类为了让客户端获取它自身的一个实例,最传统的方法是提供一个公有的构造器。除此之外,类可以提供一个公有的静态工厂方法,它只是一个返回类的实例的静态方法。

/**
 * 一个来自Boolean(基本类型boolean的装箱类)的简单示例,
 * 该方法将boolean基本类型值转换成了一个Boolean对象引用。
 */
public static Boolean vauleOf(boolean b) {
    return b ? Boolean.TURE : Boolean.FALSE;
}

提供静态工厂方法而不是公有的构造器,这样做既有优势,也有劣势。

继续阅读“第2章 创建和销毁对象”

LeetCode

2、两数相加

链表结构与树的结构类似,但是链表只有节点值和下一节点两个属性,因此除了递归,也可以通过循环来遍历。

3、无重复字符的最长子串

滑动窗口

4、寻找两个有序数组的中位数

奇偶数判断:

  • 通过 a % 2 != 0 来判断
  • 通过 ( a & 1) == 1 来判断(推荐使用,位操作,性能更优)
  • 通过 a % 2 == 1 来判断是错误的,原因:负奇数对 2 取余的结果为 -1

6、Z 字形变换

  1. 实际行数为设定的行数与字符串字符数中的最小值
  2. 每一行都用一个StringBuilder来存放,所有的StringBuilder存放在ArrayList中
  3. 依次遍历字符串,使用当前行和当前方向这两个变量对合适的行进行跟踪
  4. 只有当我们向上移动到最上面的行或向下移动到最下面的行时,当前方向才会发生改变
List list = new ArrayList(5);  

ArrayList 构造时可以指定初始容量,但此时 list.size() = 0,因为 size() 返回的是当前 list 中有多少个元素在存放,需要通过 list.add() 方法向 list 中添加元素。

ArrayList 每次新增一个元素,就会检测 ArrayList 的当前容量是否已经到达临界点,如果到达临界点则会扩容 2 倍。然而ArrayList的扩容以及数组的拷贝生成新的数组是相当耗资源的。因此, 对于已知的情景,请为集合指定初始容量。

继续阅读“LeetCode”

策略模式

书中以模拟鸭子的应用为例引出策略模式,方便理解。

模拟鸭子游戏:游戏中会出现各种鸭子,一边游泳戏水,一边呱呱叫。此系统设计了一个鸭子超类(Superclass),并让各种鸭子继承此超类。

abstract class Duck {
/**
* 所有的鸭子都会呱呱叫(Quack)也会游泳(Swim),
* 所以由超类负责处理这部分的实现代码。
*/
public void quack() {};
public void swim() {};
//每一种鸭子的外观都不同,所以display()方法是抽象的。
public abstract void display();
}
/**
* 每个鸭子子类型(subtype)负责实现自己的display()行为,
* 在屏幕上显示其外观。
*/
class MallardDuck extends Duck {
@Override
public void display() {
//外观是绿头
}
}
/**
* 许多其他类型的鸭子继承Duck类。
*/
class RedheadDuck extends Duck {
@Override
public void display() {
//外观是红头
}
}
继续阅读“策略模式”

Java多线程机制

一、线程的基本概念
1. 线程是一个程序内部的顺序控制流。
2. 线程和进程的区别
①每个进程都有独立的代码和数据空间(进程上下文),进程间的切换会有较大的开销。
②线程可以看成是轻量级的进程,同一类线程共享代码和数据空间,每个线程有独立的运行栈和程序计数器(PC),线程切换的开销小。
③多进程:在操作系统中能同时运行多个任务(程序)
④多线程:在同一应用程序中有多个顺序流同时执行
3. Java的线程是通过java.lang.Thread类来实现的。
4. VM启动时会有一个由主方法(public static void main() {})所定义的线程。
5. 可以通过创建Thread的实例来创建新的线程。
6. 每个线程都是通过某个特定Thread对象所对应的方法run()来完成其操作的,方法run()称为线程体。
7. 通过调用Thread类的start()方法来启动一个线程。

二、线程的创建和启动
1. 可以有两种方法创建新的线程
①第一种

1.定义线程类实现Runnable接口
2.Thread myThread = new Thread(target); //target为Runnable接口类型
3.Runnable中只有一个方法:
public void run();用以定义线程运行体。
4.使用Runnable接口可以为多个线程提供共享的数据。
5.在实现Runnable接口的类的run方法定义中可以使用Thread的静态方法:
public static Thread currentThread();获取当前线程的引用。

②第二种

1.可以定义一个Thread的子类并重写其run方法,如:
class MyThread extends Thread {
public void run() {
...
}
}
2.然后生成该类的对象:
MyThread myThread = new MyThread(...);san
继续阅读“Java多线程机制”

Java流

一、Java流式输入/输出原理

在Java程序中,对于数据流的输入/输出操作以“流”(stream)方式进行。
JDK提供了各种各样的“流”类,用以获取不同种类的数据。
程序中通过标准的方法输入或输出数据。

二、输入/输出流的分类
1. java.io包中定义了多个流类型(类或抽象类)来实现输入/输出功能,可以从不同的角度对其进行分类:
①按数据流的方向不同可以分为输入流和输出流。
②按处理数据单元不同可以分为字节流和字符流。
③按功能不同可以分为节点流和处理流。
2. JDK所提供的所有流类型位于包java.io内都分别继承自以下四种抽象流类型:

字节流字符流
输入流InputStreamReader
输出流OutputStreamWriter
继续阅读“Java流”