《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、整形
类型 | 字节 | 取值范围 | 封装器类 |
---|---|---|---|
byte | 1 | -128(-2^7)~ 127(2^7-1) | Byte |
short | 2 | -32768(-2^15)~ 32767(2^15 - 1) | Short |
int | 4 | -2,147,483,648(-2^31)~ 2,147,483,647(2^31 - 1) | Integer |
long | 8 | -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、浮点型
类型 | 字节 | 取值范围 | 封装器类 |
---|---|---|---|
float | 4 | 大约 ± 3.402 823 47E+38F (有效位数为6 ~ 7 位) | Float |
double | 8 | 大约 ± 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 包中的两个很有用的类: BigInteger 和 BigDecimal 。这两个类可以处理包含任意长度数字序列的数值。
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 类的一个实例。
异常可分为 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开发者 - 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 程序,这里就不看了。
第十四章 并发
这部分大概地看了下,后面再看本书仔细学习。
评论 (0)