《Java核心技术》读书笔记

Author Avatar
wshunli 4月 07, 2018
  • 在其它设备中阅读本文章

《Java核心技术》读书笔记

前面阅读了《Java 编程思想》,可能是水平不够吧,感觉读起来很吃力,这次拿 《Java核心技术》 复习下。

《JAVA编程思想》 和 《JAVA核心技术Ⅰ》 看哪本? - 知乎
https://www.zhihu.com/question/29745861

这次就不再那么详细地记录了。

第一章 Java 程序设计概述

主要介绍了 Java 程序的平台:Java SE、Java EE、Java ME 及 Java 发展简史。

第二章 Java 程序设计环境

主要介绍了 Java 环境的搭建以及常用命令行工具。

javac Main.java
java Main

javac 是 Java 编译器,将 Main.java 源代码编译为 Main.class ;
java 启动 Java 虚拟机,虚拟机执行 class 文件中的字节码。

然后介绍了 图形化应用程序 和用于浏览器的 applet 技术。

第三章 Java 的基本程序设计结构

Hello World

package com.wshunli;
public class Main {
    public static void main(String[] args) {
        // write your code here
        System.out.println("Hello World !");
    }
}

还是得记住这段的,得能手写出来。

基本数据类型

Java 语言提供了八种基本类型。六种数字类型(四个整数型,两个浮点型),一种字符类型,还有一种布尔型。

1、整形

类型字节取值范围封装器类
byte1-128(-2^7)~ 127(2^7-1)Byte
short2-32768(-2^15)~ 32767(2^15 - 1)Short
int4-2,147,483,648(-2^31)~ 2,147,483,647(2^31 - 1)Integer
long8-9,223,372,036,854,775,808(-2^63)~ 9,223,372,036,854,775,807(2^63 -1)Long

long 长整型数值有一个后缀 L 或 l ( 如 0L )

2、浮点型

类型字节取值范围封装器类
float4大约 ± 3.402 823 47E+38F (有效位数为6 ~ 7 位)Float
double8大约 ± 1.797 693 134 862 315 70E+308 (有效位数为15 位)Double

float 浮点型数值有一个后缀 F 或 f ( 如 0.0f )
double 双精度型数值有一个后缀 D 或 d ( 如 0.0d )

3、字符类型

在 Java 中,char 类型描述了 UTF-16 编码中的一个代码单元。

4、布尔型

boolean ( 布尔)类型有两个值: false 和true,用来判定逻辑条件。

整型值和布尔值之间不能进行相互转换。

变量

变量的声明及初始化,不区分变量的声明与定义。

int vacationDays;
vacationDays = 12;

1、常量

在 Java 中, 利用关键字 final 指示常量。

final double CM_PER_INCH = 2.54;

关键字 final 表示这个变量只能被赋值一次。习惯上,常量名使用全大写。

浅谈Java中的final关键字 - 平凡希 - 博客园
https://www.cnblogs.com/xiaoxi/p/6392154.html

2、类常量、静态常量

static 关键字用来声明独立于对象的静态变量,无论一个类实例化多少对象,它的静态变量只有一份拷贝。

public static final double CM_PER_INCH = 2.54;

可以在一个类或者其他类中的多个方法中使用。

运算符

Java 中的数据是通过使用操作符来操作的。

  • 赋值操作符
  • 算术操作符
  • 关系操作符
  • 逻辑操作符
  • 按位操作符
  • 其他操作符

1、常用的数学计算函数

JAVA Math 类的数学计算函数 - CSDN博客
https://blog.csdn.net/antares_santalen/article/details/52451807

2、数值类型之间的转换

Java 中数值类型之间的转换方式有两种:一是自动类型转换,另一种是强制类型转换。

自动类型转换

数值类型之间的转换

实线表示无信息丢失的转换,虚线表示可能有精度丢失的转换。

如果两个操作数中有一个是 double 、float 、long 类型,别一个操作数就会转换为相同类型。
否则,两个操作数都将被转换为 int 类型。

强制类型转换 的格式是在需要转型的数据前加上“( )”,然后在括号内加入需要转化的数据类型。
有的数据经过转型运算后,精度会丢失,而有的会更加精确。

字符串

字符串也是相当重要的概念,Java 字符串就是 Unicode 字符序列。

1、Java String 的静态方法

static String valueOf​(Object obj)
static String format​(Locale l, String format, Object... args)
static String copyValueOf​(char[] data)
static String format​(String format, Object... args)
static String join​(CharSequence delimiter, CharSequence... elements)

2、Java String 的主要实例方法

// 获取子串
String substring​(int beginIndex)
String substring​(int beginIndex, int endIndex)
// 字符串是否相等
boolean equals​(Object anObject)
boolea equalsIgnoreCase​(String anotherString)
// 替换字符
String replace​(char oldChar, char newChar)
String replaceAll​(String regex, String replacement)
String replaceFirst​(String regex, String replacement)
// 连接
String concat​(String str)
// 切分
String[] split​(String regex)
// 所有字符转换大小写
String toLowerCase()
String toUpperCase()
// 去除开头结尾的空格
String trim()

涉及到内容也比较多,官方 API 文档:
https://docs.oracle.com/javase/10/docs/api/java/lang/String.html

3、构建字符串

直接使用字符串拼接效率比较低,因为每次连接字符串, 都会构建一个新的 String 对象,既耗时, 又浪费空间。

可以使用 StringBuilder 避免这个问题:

StringBuilder builder = new StringBuilder();
builder.append("Hello");
builder.append("world");
String string = builder.toString();

参考 API 文档:
https://docs.oracle.com/javase/10/docs/api/java/lang/StringBuffer.html
https://docs.oracle.com/javase/10/docs/api/java/lang/StringBuilder.html

String、StringBuffer 和 StringBuilder 的区别

首先说运行速度,或者说是执行速度,在这方面运行速度快慢为:StringBuilder > StringBuffer > String;
其次在线程安全上,StringBuilder 是线程不安全的,而 StringBuffer 是线程安全的。

  • String:适用于少量的字符串操作的情况
  • StringBuilder:适用于单线程下在字符缓冲区进行大量操作的情况
  • StringBuffer:适用多线程下在字符缓冲区进行大量操作的情况

Java中的String,StringBuilder,StringBuffer三者的区别 - 酥风 - 博客园
https://www.cnblogs.com/su-feng/p/6659064.html

输入输出

1、读取控制台输入,也经常用到,使用 Scanner 类。

Scanner scanner = new Scanner(System.in);
if (scanner.hasNext()) {
    String str = scanner.next();
}

2、格式化输出

使用 System.out.printf() 格式化输出:

System.out.printf("%,.2f", 10000.0 / 3);

转换符

用于 printf 的转换符。

3、文件的输入输出

try {
    Scanner in = new Scanner(Paths.get("niyflle.txt"), "UTF-8");
    PrintWriter out = new PrintWriter("myfile.txt", "UTF-8");
} catch (IOException e) {
    e.printStackTrace();
}

流程控制

主要介绍条件语句和循环结构控制流程。

大数值

如果基本的整数和浮点数精度不能够满足需求, 那么可以使用 java.math 包中的两个很有用的类: BigIntegerBigDecimal 。这两个类可以处理包含任意长度数字序列的数值。
BigInteger 类实现了任意精度的整数运算, BigDecimal 实现了任意精度的浮点数运算。

数组

数组是一种数据结构, 用来存储同一类型值的集合。

1、匿名数组:

int[] smallPrimes = {2, 3, 5, 7, 11, 13};
smallPrimes = new int[]{17, 19, 23, 29, 31, 37};

2、数组拷贝

数组变量拷贝直接赋值即可,这样两个变量将引用同一数组。

如果希望将一个数组的所有值拷贝到一个新的数组中去,就要使用 Arrays 类的 copyOf 方法。

int[] copyOfSmallPrimes = Arrays.copyOf(smallPrimes, smallPrimes.length);

3、数组排序

要想对数值型数组进行排序, 可以使用 Arrays 类中的 sort 方法:

Arrays.sort(smallPrimes);

4、命令行参数

每一个Java 应用程序都有一个带 String arg[] 参数的 main 方法。
这个参数表明 main 方法将接收一个字符串数组, 也就是命令行参数。

例如:

public class Message {
    public static void main(String[] args) {
        if (args.length == 0 || args[0].equals("-h"))
            System.out.print("Hello, ");
        else if (args[0].equals("-g"))
            System.out.print("Goodbye,");
        // print the other command-line arguments
        for (int i = 1; i < args.length; i++)
            System.out.print(" " + args[i]);
        System.out.println("!");
    }
}

运行时输入如下命令:

java Message -g cruel world

结果:

Goodbye,cruel world!

5、多维数组

6、不规则数组

第四章 对象与类

1、类、对象及类之间的关系。

类之间常见的关系:依赖(“ uses-a ”)、聚合(“ has-a ”)、继承(“ is-a ”)

2、预定义类和用户自定义类。

3、静态域和静态方法

4、方法参数

Java 程序设计语言总是采用按值调用。(引用调用表示方法接收的是调用者提供的变量地址。)

也就是说, 方法得到的是所有参数值的一个拷贝,方法结束就丢弃该拷贝,特别是,方法不能修改传递给它的任何参数变量的内容。

然而,方法参数共有两种类型:基本数据类型、对象引用。

对于基本数据类型,方法只是获得参数值的拷贝,无法修改实参的值;
对于对象的引用,方法同样得到对象引用的拷贝,该拷贝和对象引用同时引用同一对象,则可以修改对象的状态。

private static void swap(Employee x, Employee y) {
    Employee temp = x;
    x = y;
    y = temp;
}

Employee a = new Employee("wshunli1", 10);
Employee b = new Employee("wshunli2", 15);
swap(a, b);

结果 对象 a 和 b 并未交换,只是交换了各自引用的拷贝,方法结束就丢弃了。

总结:

  • 一个方法不能修改一个基本数据类型的参数(即数值型或布尔型)。
  • 一个方法可以改变一个对象参数的状态。
  • 一个方法不能让对象参数引用一个新的对象。

5、对象构造

主要介绍了类的构造方法、初始化顺序以及初始化块、对象析构的概念。

6、包和类的路径

7、文档注释

第五章 继承

类、超类和子类

主要介绍了子类和父类之间的关系,方法的覆盖,多态,抽象类相关内容等。

1、对象包装器与自动装箱

基本数据类型都有一个与之对应的类。

Integer、Long、Float、Double、Short、Byte、Character 、Void 和 Boolean 。

2、枚举类

public enum Size { SMALL, MEDIUM, LARGE, EXTRAJARGE };

3、泛型数组列表

其实就是 java 集合的相关内容,主要是 ArrayList 。

Object 所有类的超类

Object 类是 Java 中所有类的始祖, 在 Java 中每个类都是由它扩展而来的。

1、equals 方法

Object 类中的 equals 方法用于检测一个对象是否等于另外一个对象。
在 Object 类中,这个方法将判断两个对象是否具有相同的引用。

2、hashCode 方法

散列码( hash code ) 是由对象导出的一个整型值。散列码是没有规律的。
如果x 和y 是两个不同的对象, x.hashCode( ) 与y.hashCode( ) 基本上不会相同。

Java hashCode() 和 equals()的若干问题解答 - 如果天空不死 - 博客园
http://www.cnblogs.com/skywang12345/p/3324958.html

3、toString 方法

在 Object 中还有一个重要的方法, 就是 toString 方法, 它用于返回表示对象值的字符串。

参数数量可变的方法

在参数列表中 Object… 参数类型与 Object[] 完全一样。

public static double max(double... values) {
    double largest = Double.NEGATIVE_INFINITY;
    for (double v : values) if (v > largest) largest = v;
    return largest;
}

反射

反射库 ( reflection library ) 提供了一个非常丰富且精心设计的工具集, 以便编写能够动态操纵 Java 代码的程序。

1、Class 类

在程序运行期间,Java 运行时系统始终为所有的对象维护一个被称为运行时的类型标识。
这个信息跟踪着每个对象所属的类。保存这些信息的类被称为 Class

获取 Class 对象有三种方法:

Employee employee = new Employee();
Class class1 = employee.getClass();
System.out.println(class1.getName());
try {
    Class class2 = Class.forName("com.wshunli.Employee");
    Class class3 = Employee.class;
    System.out.println(class2.getName());
    System.out.println(class3.getName());
} catch (ClassNotFoundException e) {
    e.printStackTrace();
}

可以使用 Class 的方法获取相关信息,如使用 newlnstance( ) 可以用来动态地创建一个类的实例:

try {
    Object o = class1.newInstance();
} catch (InstantiationException | IllegalAccessException e) {
    e.printStackTrace();
}

2、利用反射分析类的能力

在 java.lang.reflect 包中有三个类 Field、Method 和 Constructor 分别用于描述类的域、方法和构造器。
三个类中都有 getName 、getType 、getModifiers 方法,分别返回 名字、返回类型、及标识符。

Field[] fields = Integer.class.getFields();
for (Field filed : fields) {
    String f = Modifier.toString(filed.getModifiers()) + filed.getType() + filed.getName();
    System.out.println(f);
}

Class 类中的 getFields、getMethods 和 getConstructors 方法将分别返回类提供的 public 域、方法和构造器数组, 其中包括超类的公有成员。
Class 类的 getDeclareFields、getDeclareMethods 和 getDeclaredConstructors 方法将分别返回类中声明的全部域、方法和构造器, 其中包括私有和受保护成员,但不包括超类的成员。

3、在运行时使用反射分析对象

在运行时使用反射分析对象方法主要是使用 Field 等的 get、set 方法。

Employee employee = new Employee("wshunli", 15);
Class class1 = employee.getClass();
try {
    Field name = class1.getDeclaredField("name");
    name.setAccessible(true);
    System.out.println(name.get(employee).toString());
    name.set(employee, "wshunli.com");
    System.out.println(name.get(employee).toString());
} catch (NoSuchFieldException | IllegalAccessException e) {
    e.printStackTrace();
}

4、调用任意方法

反射机制允许调用任意方法。在 Method 类中有一个 invoke 方法, 它允许调用包装在当前 Method 对象中的方法。

invoke 方法的签名是:

Object invoke(Object obj, Object... args)

第一个参数为实例对象,静态方法可以设置为 null;
第二个参数为方法得参数;
返回值需完成类型转换。

try {
    Method method = Math.class.getMethod("sqrt", double.class);
    double result = (double) method.invoke(null, 3);
    System.out.println(result);
} catch (NoSuchMethodException | InvocationTargetException | IllegalAccessException e) {
    e.printStackTrace();
}

第六章 接口、lambda 表达式与内部类

接口及其示例

1、接口的概念及特点

在 Java 程序设计语言中, 接口不是类,而是对类的一组需求描述,这些类要遵从接口描述的统一格式进行定义。

public interface Comparable {
    int compareTo(Object other);
}
// 使用泛型
public interface Comparable<T> {
    int compareTo(T other);
}

接口的特点:
Java 单继承,只能继承一个(抽象)类,单个类可以实现多个接口;
Java 强类型语言,接口可以保证接口的实现类都实现了接口的方法。

接口中的成员函数默认为 public ,成员变量默认设置成 public static final ;

接口不能包含静态方法和实例对象(在 Java SE 8 以前),但能包含静态成员变量(默认为常量)。

2、静态方法

在 Java SE 8 中,允许在接口中增加静态方法。

public interface Path {
    public static Path get(String first, String... more) {
        return FileSystems.getDefault().getPath(first, more);
    }
}

3、默认方法及其冲突

可以为接口方法提供一个默认实现。必须用 default 修饰符标记这样一个方法。

public interface Comparable<T> {
    default int compareTo(T other) {
        return 0;
    }
}

使用默认方法,接口的实现类就不必实现该方法了。

如果先在一个接口中将一个方法定义为默认方法, 然后又在超类或另一个接口中定义了同样的方法。
解决默认方法冲突:
超类优先。如果超类提供了一个具体方法, 同名而且有相同参数类型的默认方法会被忽略。
接口冲突。如果一个超接口提供了一个默认方法, 另一个接口提供了一个同名而且参数类型(不论是否是默认参数)相同的方法, 必须覆盖这个方法来解决冲突。

以上为两个接口的命名冲突,
对于继承一个超类又同时实现一个接口的类,并且超类和接口存在相同的方法,只会考虑超类方法

lambda 表达式

以前也知道这个概念,书上说得有点绕。

Lambda 表达式有何用处?如何使用? - Sevenvidia的回答 - 知乎
https://www.zhihu.com/question/20125256/answer/324121308

Lambda 表达式,也可称为闭包,它是推动 Java 8 发布的最重要新特性。
Lambda 允许把函数作为一个方法的参数(函数作为参数传递进方法中)。

Java8 lambda表达式10个示例 - ImportNew
http://www.importnew.com/16436.html

Lambda 表达式本身就是一个接口的实现。

内部类

内部类( inner class ) 是定义在另一个类中的类。

  • 内部类方法可以访问该类定义所在的作用域中的数据, 包括私有的数据。
  • 内部类可以对同一个包中的其他类隐藏起来。
  • 当想要定义一个回调函数且不想编写大量代码时,使用匿名(anonymous) 内部类比较便捷。

1、使用内部类访问对象状态

2、内部类的特殊语法

outerObject.this.para;
outerObject.new InnerClass(construction parameters);
OuterClass.InnerClass;

3、局部内部类、匿名内部类、静态内部类

代理

利用代理可以在运行时创建一个实现了一组给定接口的新类。

说说 JAVA 代理模式 - ImportNew
http://www.importnew.com/26116.html

这里主要讲的是动态代理

// 抽象角色
public interface ICoder {
    public void implDemands(String demandName);
}
// 真实角色
public class JavaCoder implements ICoder {
    private String name;
    public JavaCoder(String name) {
        this.name = name;
    }
    @Override
    public void implDemands(String demandName) {
        System.out.println(name + " implemented demand:" + demandName + " in JAVA!");
    }
}
// 调用处理器
public class TraceHandler implements InvocationHandler {
    private Object object;
    public TraceHandler(Object object) {
        this.object = object;
    }
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println(System.currentTimeMillis());
        Object result = method.invoke(object, args);
        System.out.println(System.currentTimeMillis());
        return result;
    }
}
// 动态代理
public class DynamicClient {
    public static void main(String[] args) {
        ICoder coder = new JavaCoder("wshunli");
        ClassLoader classLoader = coder.getClass().getClassLoader();
        Class[] interfaces = coder.getClass().getInterfaces();
        TraceHandler traceHandler = new TraceHandler(coder);
        ICoder proxy = (ICoder) Proxy.newProxyInstance(classLoader, interfaces, traceHandler);
        proxy.implDemands("test");

    }
}

要想创建一个代理对象, 需要使用 Proxy 类的 newProxylnstance 方法。这个方法有三个参数:

  • 一个类加栽器( class loader)。
  • 一个 Class 对象数组, 每个元素都是需要实现的接口。
  • 一个调用处理器。

第七章 异常、断言和日志

对于异常情况,Java 使用一种称为异常处理( exception handing) 的错误捕获机制处理。

异常

1、处理错误

在 Java 程序设计语言中, 异常对象都是派生于 Throwable 类的一个实例。

Java中的异常分类

异常可分为 Error 、Exception 两类。

Error 描述了 Java 运行时系统的内部错误和资源耗尽错误。应用程序不应该抛出这种类型的对象。
Exception 又分解为两个分支:一个分支派生于 RuntimeException ,由程序错误导致的异常; 另一个分支包含其他异常,程序本身没有问题,但由于像 I/O 错误这类问题导致的异常。

Java 语言规范将派生于 Error 类或 RuntimeException 类的所有异常称为非受查( unchecked ) 异常, 所有其他的异常称为受查 ( checked ) 异常。这

2、抛出异常

一个方法必须声明所有可能抛出的受查异常, 而非受查异常要么不可控制 ( Error),要么就应该避免发生 ( RuntimeException )。

《Java编程思想》读书笔记(五) | CirGIS
https://www.wshunli.com/posts/b6f25079.html

4、捕获异常

如果某个异常发生的时候没有在任何地方进行捕获,那程序就会终止执行,并在控制台上打印出异常信息, 其中包括异常的类型和堆栈的内容。

使用断言

编写代码时,我们总是会做出一些假设,断言就是用于在代码中捕捉这些假设。

断言可以有两种形式:
1.assert Expression1
2.assert Expression1:Expression2

当执行代码时,使用 -ea 选项使断言有效,也可以使用 -da 选项使断言无效(默认为无效)。

记录日志

要生成简单的日志记录,可以使用全局日志记录器(global logger) 并调用其 info 方法:

Logger.getGlobal().info("start record log");
Logger.getGlobal().setLevel(Level.OFF);

日志的级别:SEVERE (highest value)、WARNING、INFO、CONFIG、FINE、FINER、FINEST (lowest value)

public class LoggerDemo {
    private static final Logger logger = Logger.getLogger("com.wshunli");
    public static void main(String[] args) {
        logger.setLevel(Level.FINE);
        logger.fine("Logger test1");
        logger.log(Level.FINE, "Logger test2");
    }
}

然后介绍了日志的配置、本地化、处理器、过滤器、格式化器等。

第八章 泛型程序设计

泛型程序设计 ( Generic programming ) 意味着编写的代码可以被很多不同类型的对象所重用。

1、为什么要使用泛型程序设计

类型参数,其中 String 为类型参数:

ArrayList<String> list = new ArrayList<>();

类型参数的魅力在于:使得程序具有更好的可读性和安全性。

2、泛型类 ( generic class ) 就是具有一个或多个类型变量的类。

public class Holder1<T, U> {
    private T a;
    private U b;
    public Holder1(T a, U b) {
        this.a = a;
        this.b = b;
    }
}

在 Java 库中, 使用变量 E 表示集合的元素类型, K 和 V 分别表示表的关键字与值的类型。
T ( 需要时还可以用临近的字母 U 和 S ) 表示 “任意类型”。

类型变量的限定:

class Holder2<T extends Comparable> {
    private T a;
    public Holder2(T a) {
        this.a = a;
    }
}
class Holder3<T extends Comparable & Serializable> {
    private T a;
    public Holder3(T a) {
        this.a = a;
    }
}

表示 T 应该是绑定类型的子类型。T 和绑定类型可以是类, 也可以是接口,均使用 extends 关键字。

3、泛型方法

public <T> void f(T x) {
    System.out.println(x.getClass().getName());
}

4、泛型代码和虚拟机

虚拟机没有泛型类型对象 — 所有对象都属于普通类。

类型擦除:在虚拟机中,擦除类型变量, 并替换为限定类型 ( 无限定的变量用 Object )。

后面讲了泛型的约束和局限性。

5、通配符类型

通配符类型中,允许类型参数变化。

Pair<? extends Employee>

通配符的超类型限定:

通配符限定与类型变量限定十分类似,但是,还有一个附加的能力, 即可以指定一个超类型限定, 如下所亦:

Pair<? super Employee>

无限定通配符:

Pair<?>

第九章 集合

Java 集合框架

在 Java 类库中,集合类的基本接口是 Collection 接口。

很重要的接口 迭代器 Iterator

public interface Iterator<E> {
    boolean hasNext();
    E next();
    default void remove() {
        throw new UnsupportedOperationException("remove");
    }
    default void forEachRemaining(Consumer<? super E> action) {
        Objects.requireNonNull(action);
        while (hasNext())
            action.accept(next());
    }
}

应该将 Java 迭代器认为是位于两个元素之间。当调用 next 时, 迭代器就越过下一个元素,并返回刚刚越过的那个元素的引用。

《Java编程思想》读书笔记(四) | CirGIS
https://www.wshunli.com/posts/d96c953e.html

Java 集合

Java 集合

java集合类 - java开发者 - SegmentFault 思否
https://segmentfault.com/a/1190000008522388
由浅入深理解java集合(一)——集合框架 Collection、Map - 简书
https://www.jianshu.com/p/589d58033841

40个Java集合面试问题和答案 - ImportNew
http://www.importnew.com/15980.html

第十章到十三章

只要介绍使用图形用户界面的 Java 程序,这里就不看了。

第十四章 并发

这部分大概地看了下,后面再看本书仔细学习。

如果本文对您有所帮助,且您手头还很宽裕,欢迎打赏赞助我,以支付网站服务器和域名费用。 https://paypal.me/wshunli 您的鼓励与支持是我更新的最大动力,我会铭记于心,倾于博客。
本文链接:https://www.wshunli.com/posts/9c87288c.html