第三章:Java的基本程序设计结构
一、基本数据类型
列举
byte(字节型)、short(短整型)、int(整型)、long(长整型)、float(单精度浮点型)、double(双精度浮点型)、boolean(布尔型)、char(字符型)
对应包装类
java.lang.Byte、java.lang.Short、java.lang.Integer、java.lang.Long、java.lang.Float、java.lang.Double、java.lang.Boolean、java.lang.Character
详细划分
具体可分为四类
整型 byte short int long
浮点型 float double
逻辑型 boolean(它只有两个值可取 true false)
字符型 char
二、运算符
与其他编程语言基本一样
三、字符串
1、String类
字符串查找
String提供了两种查找字符串的方法,即indexOf与lastIndexOf方法。
1、indexOf(String s)
该方法用于返回参数字符串s在指定字符串中首次出现的索引位置,当调用字符串的indexOf()方法时,会从当前字符串的开始位置搜索s的位置;如果没有检索到字符串s,该方法返回-1
1 String str ="We are students"; 2 int size = str.indexOf("a"); // 变量size的值是3
2、lastIndexOf(String str)
该方法用于返回字符串最后一次出现的索引位置。当调用字符串的lastIndexOf()方法时,会从当前字符串的开始位置检索参数字符串str,并将最后一次出现str的索引位置返回。如果没有检索到字符串str,该方法返回-1.
如果lastIndexOf方法中的参数是空字符串"" ,,则返回的结果与length方法的返回结果相同。
获取指定索引位置的字符
使用charAt()方法可将指定索引处的字符返回。
1 String str = "hello word"; 2 char mychar = str.charAt(6); // mychar的结果是w
获取子字符串
通过String类的substring()方法可对字符串进行截取。这些方法的共同点就是都利用字符串的下标进行截取,且应明确字符串下标是从0开始的。在字符串中空格占用一个索引位置。
1、substring(int beginIndex)
该方法返回的是从指定的索引位置开始截取知道该字符串结尾的子串。
1 String str = "Hello word"; 2 String substr = str.substring(3); //获取字符串,此时substr值为lo word
2、substring(int beginIndex, int endIndex)
beginIndex : 开始截取子字符串的索引位置
endIndex:子字符串在整个字符串中的结束位置
1 String str = "Hello word"; 2 String substr = str.substring(0,3); //substr的值为hel
去除空格
trim()方法返回字符串的副本,忽略前导空格和尾部空格。
字符串替换
replace()方法可实现将指定的字符或字符串替换成新的字符或字符串
oldChar:要替换的字符或字符串
newChar:用于替换原来字符串的内容
如果要替换的字符oldChar在字符串中重复出现多次,replace()方法会将所有oldChar全部替换成newChar。需要注意的是,要替换的字符oldChar的大小写要与原字符串中字符的大小写保持一致。
1 String str= "address"; 2 String newstr = str.replace("a", "A");// newstr的值为Address
判断字符串的开始与结尾
startsWith()方法与endsWith()方法分别用于判断字符串是否以指定的内容开始或结束。这两个方法的返回值都为boolean类型。
1、startsWith(String prefix)
该方法用于判断当前字符串对象的前缀是否是参数指定的字符串。
2、endsWith(String suffix)
该方法用于判断当前字符串是否以给定的子字符串结束
判断字符串是否相等
1、equals(String otherstr)
如果两个字符串具有相同的字符和长度,则使用equals()方法比较时,返回true。同时equals()方法比较时区分大小写。
2、equalsIgnoreCase(String otherstr)
equalsIgnoreCase()方法与equals()类型,不过在比较时忽略了大小写。
按字典顺序比较两个字符串
compareTo()方法为按字典顺序比较两个字符串,该比较基于字符串中各个字符的Unicode值,按字典顺序将此String对象表示的字符序列与参数字符串所表示的字符序列进行比较。如果按字典顺序此String对象位于参数字符串之前,则比较结果为一个负整数;如果按字典顺序此String对象位于参数字符串之后,则比较结果为一个正整数;如果这两个字符串相等,则结果为0.
1 str.compareTo(String otherstr);
字母大小写转换
字符串的toLowerCase()方法可将字符串中的所有字符从大写字母改写为小写字母,而tuUpperCase()方法可将字符串中的小写字母改写为大写字母。
1 str.toLowerCase(); 2 str.toUpperCase();
字符串分割
使用split()方法可以使字符串按指定的分隔字符或字符串对内容进行分割,并将分割后的结果存放在字符数组中。
1 str.split(String sign);
sign为分割字符串的分割符,也可以使用正则表达式。
没有统一的对字符串进行分割的符号,如果想定义多个分割符,可使用符号“|”。例如,“,|=”表示分割符分别为“,”和“=”。
1 str.split(String sign, in limit);
该方法可根据给定的分割符对字符串进行拆分,并限定拆分的次数。
2、StringBuilder类和StringBuffer类
StringBuilder和StringBuffer一样,都是继承自抽象类AbstractStringBuilder类,也是一个可变的字符序列。StringBuilder和StringBuffer非常相似,甚至有互相兼容的API,不过,StringBuilder不是线程安全的,这是和StringBuffer的主要区别 。
内部方法很多与String类一样,具体可以查看javadocs
四、数组
1)数组在Java中是一个对象,数组实例同样是使用new操作符创建的。Array.length指定了数组长度,例如:
1 2 3 4 |
int[] intArray = new int[10]; System.out.println(intArray.length) Output: 10 |
Array.length 表示数组的容量,只要数组创建了,每一个索引被初始化为默认值。
2)数组索引起始为0,负数索引在Java中是无效的,会抛出ArrayIndexOutOfBoundException ,如果你尝试用无效的索引访问数组,这个无效的索引可能是一个负索引,或者是大于等于数组长度的索引。
3)数组存储在Java堆的连续内存空间,所以如果你创建一个大的索引,你可以有足够的堆空间直到抛出OutofmemoryError,因为请求的内存大小在连续的内存空间不可用。
4)数组一个固定长度 的数据结构,一旦声明,你不能改变数组的长度。
5)不同类型的数组有不同的类型,例如下面例子,intArray.getClass()不同于floatArray.getClass()
1 2 |
int[] intArray = new int[10]; float[] floatArray = new float[10]; |
1 |
6)你不能存储double值在int数组中,否则导致编译错误。 |
1 2 |
int[] intArray = new int[10]; int Array[5]=1.2; //compilation error |
如果尝试在运行时做这个操作,那么Java抛出ArrayStoreException
7)在Java数组中可以有不同方式的创建方式,这里就是创建数组的例子:
1 2 3 4 |
int[] intArray; //creating array without initializing or specifying size int intArray1[]; //another int[] reference variable can hold reference of an integer array int[] intArray2 = new int[10]; //creating array by specifying size int[] intArray3 = new int[]{1,2,3,4}; //creating and initializing array in same line. |
你既可以选择在创建数组的时候初始化数组,也可以以后通过for循环初始化,中括号既可以在变量的前面也可以在变量后面。
第一种方法是方便的创建多个数组如:
int[] array1, array2
这里的array1和array2是整型数组,而第二种方法你需要放两次括号如:
int array1[], array2[]
尽管在风格上没有很多不同,我读“int[] ”叫int数组,这种写法更容易被理解。
8)如果没有明确的初始化数组元素,那么数组就会用默认的类型值初始化,例如假若没有初始化整型数组,元素都将默认值为0,没有初始化的boolean值是false,对象数组是null。
9)你可以通过使用[]操作符访问数组元素,因为数组索引起始于0,[0]返回第一个元素,[length-1]返回最后一个元素,for循环是一种迭代整个数组便捷方法。你可以使用for循环初始化整个数组、访问的每个索引或更新、获取数组元素。Java5同样提供了加强的for循环,数组自己管理索引,防止ArrayIndexOutOfBoundException,这里是一个迭代的例子:
传统的方式:
1 2 3 4 5 6 7 8 9 10 11 12 |
int[] numbers = new int[]{10, 20, 30, 40, 50}; for (int i = 0; i < numbers.length; i++) { System.out.println("element at index " + i + ": " + numbers[i]); } Output: element at index 0: 10 element at index 1: 20 element at index 2: 30 element at index 3: 40 element at index 4: 50 |
加强的for循环
1 2 3 4 5 6 7 8 9 10 |
for(int i: numbers){ System.out.println(i); } Output: 10 20 30 40 50 |
正如你看到的,加强的for循环不需要检查数组索引,如果你想逐个地访问所有的元素这是一种很好的方法,但是同时因为你不能访问索引,你就不能修改数组元素。
10)Java中数组可以轻易的转换成ArrayList。ArrayList一个基于索引的集合,它是作为数组的备选方案。ArrayList的优点是可以改变容量大小,只需要创建个更大的数组然后拷贝内容到新数组,但你不能改变数组的大小。
11)Java API同样提供了一些便捷方法通过java.utils.Arrays类去操作数组,通过使用Arrays你可以排序数组,你可以做二分搜索。
12)java.lang.System类提供了实用方法拷贝元素到另一个数组。在拷贝内容从一个数组到另一个数组的时候System.arrayCopy非常强大和灵活。你可以拷贝整个或子数组,具体看你的需求。
System.arraycoy语法:
1 |
public static void arraycopy(Object src, int srcPos, Object dest, int destPos, int length) |
如你所见,arraycopy允许我们指定索引和长度,能很灵活给你拷贝子数组和存储到需要的位置或目标数组。这里是一个例子,拷贝前三个元素到目标数组:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
public static void main(String args[]) { int[] source = new int[]{10, 20, 30, 40, 50}; int[] target = new int[5]; System.out.println("Before copying"); for(int i: target){ System.out.println(i); } System.arraycopy(source, 0, target, 0, 3); System.out.println("after copying"); for(int i: target){ System.out.println(i); } } Output: Before copying 0 0 0 0 0 after copying 10 20 30 0 0 |
你可以看到拷贝之前所有元素是0,之后前三个元素被替换了。
13)Java同样支持多维数组,在表示2D和3D的时候非常有用,像行和列或矩阵。多维数组也是一个数组的数组,这里是创建多维数组的例子:
1 |
int[][] multiArray = new int[2][3]; |
这是数组有2行3列,或者说长度是2的数组中,它的每个元素里保存的是长度为3的数组,这里是初始化多维数组的例子:
1 2 3 |
int[][] multiArray = {{1,2,3},{10,20,30}}; System.out.println(multiArray[0].length); System.out.println(multiArray[1].length); |
14)数组是一种非常快的数据结构,如果你已经知道元素的长度,那么就应该使用数组而非ArrayList等数据结构。
第四章:对象与类
1、静态域
如果将域定义为static,每个类中只有一个这样的域。而每个对象对于所有的实例域缺都有一份自己的拷贝。
代码块
/**
* @author: ------
* @date: 2018/11/29
* @description:
*/
public class T {
private static int staticId = 0;
private int id;
}
现在每一个T的对象都有一个自己的id域,但是这个类的所有实例都将共享一个staticId域。即使没有一个T对象,这个staticId也是存在的。它属于类。而不属于任何一个独立的对象
2、静态常量
代码块
public class Math {
public final static double PI = 3.1415926;
}
3、静态方法
静态方法是一种不能向对象实施操作的方法。可以认为静态方法是一种没有this参数的方法(在一个非静态的方法中,this参数表示这个方法的隐式参数)。
在下面两种情况需要使用静态方法:
一个方法不需要访问对象状态,其所需参数都是通过显示参数提供
一个方法只需要访问类的静态域
4、对象析构和finalize方法
Java有自动的垃圾回收器,不需要人工回收内存,所以Java不支持析构器。某些对象使用了内存之外的其他资源,例如。文件或者使用了系统资源的另一个对象的句柄。在这种情况下,当资源不再需要的时候,将其回收和再利用将显得十分重要。可以为任何一个类添加finalize方法,finalize方法将在垃圾回收器清楚对象之前调用。在实际应用中,不要依赖于十月finalize方法回收任何短缺的资源,这是因为很难找到这个方法什么方法才能调用。
第五章 继承
1、final类和方法:阻止继承
2、多态
多态存在的三个必要条件:
一、要有继承;
二、要有重写;
三、父类引用指向子类对象。
Java中多态的实现方式:
接口实现
继承父类进行方法重写
同一个类中进行方法重载
3、枚举类
在某些情况下,一个类的对象时有限且固定的,如季节类,它只有春夏秋冬4个对象这种实例有限且固定的类,在 Java 中被称为枚举类;
在 Java 中使用 enum 关键字来定义枚举类,其地位与 class、interface 相同;
枚举类是一种特殊的类,它和普通的类一样,有自己的成员变量、成员方法、构造器 (只能使用 private 访问修饰符,所以无法从外部调用构造器,构造器只在构造枚举值时被调用);
一个 Java 源文件中最多只能有一个 public 类型的枚举类,且该 Java 源文件的名字也必须和该枚举类的类名相同,这点和类是相同的;
使用 enum 定义的枚举类默认继承了 java.lang.Enum 类,并实现了 java.lang.Seriablizable 和 java.lang.Comparable 两个接口;
所有的枚举值都是 public static final 的,且非抽象的枚举类不能再派生子类;
枚举类的所有实例(枚举值)必须在枚举类的第一行显式地列出,否则这个枚举类将永远不能产生实例。列出这些实例(枚举值)时,系统会自动添加 public static final 修饰,无需程序员显式添加。
第六章 接口、lambda表达式与内部类
接口:在Java编程语言中是一个抽象类型(Abstract Type),它被用来要求类(Class)必须实现指定的方法,使不同类的对象可以利用相同的界面进行沟通。接口通常以interface来宣告,它仅能包含方法签名(Method Signature)以及常量宣告(变量宣告包含了 static 及 final),一个接口不会包含方法的实现(仅有定义)。
接口无法被实例化,但是可以被实现。一个实现接口的类,必须实现接口内所描述的所有方法,否则就必须宣告为抽象类(Abstract Class)。另外,在Java中,接口类型可用来宣告一个变量,他们可以成为一个空指针,或是被绑定在一个以此接口实现的对象。
其中一个使用接口的优势是,可以利用他们模拟多重继承,类在JAVA中不允许多重继承,所有在JAVA中的类必须而且仅能有一个父类,而java.lang.Object(JAVA类型系统中最顶层的类型)是唯一一个例外。
JAVA的类可以被实现许多个接口,然而一个接口则无法实现其他的接口
lambda表达式:
示例1:用lambda表达式实现Runable
开始使用Java 8时,首先做的就是使用lambda表达式替换匿名类,而实现Runnable接口是匿名类的最好示例。看一下Java 8之前的runnable实现方法,需要4行代码,而使用lambda表达式只需要一行代码。我们在这里做了什么呢?那就是用() -> {}代码块替代了整个匿名类
代码块
// Java 8之前:
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("Before Java8, too much code for too little to do");
}
}).start();
//Java 8方式:
new Thread( () -> System.out.println("In Java8, Lambda expression rocks !!") ).start();
输出:
Before Java8,too much code, for too little to do
Lambda expression rocks !!
示例2:使用lambda表达式对列表进行迭代
如果你使过几年Java,你就知道针对集合类,最常见的操作就是进行迭代,并将业务逻辑应用于各个元素,例如处理订单、交易和事件的列表。由于Java是命令式语言,Java 8之前的所有循环代码都是顺序的,即可以对其元素进行并行化处理。如果你想做并行过滤,就需要自己写代码,这并不是那么容易。通过引入lambda表达式和默认方法,将做什么和怎么做的问题分开了,这意味着Java集合现在知道怎样做迭代,并可以在API层面对集合元素进行并行处理。下面的例子里,我将介绍如何在使用lambda或不使用lambda表达式的情况下迭代列表。你可以看到列表现在有了一个 forEach() 方法,它可以迭代所有对象,并将你的lambda代码应用在其中
代码块
// Java 8之前:
List features = Arrays.asList("Lambdas", "Default Method", "Stream API", "Date and Time API");
for (String feature : features) {
System.out.println(feature);
}
// Java 8之后:
List features = Arrays.asList("Lambdas", "Default Method", "Stream API", "Date and Time API");
features.forEach(n -> System.out.println(n));
// 使用Java 8的方法引用更方便,方法引用由::双冒号操作符标示,
// 看起来像C++的作用域解析运算符
features.forEach(System.out::println);
示例3:使用lambda表达式和函数式接口Predicate
除了在语言层面支持函数式编程风格,Java 8也添加了一个包,叫做 java.util.function。它包含了很多类,用来支持Java的函数式编程。其中一个便是Predicate,使用 java.util.function.Predicate 函数式接口以及lambda表达式,可以向API方法添加逻辑,用更少的代码支持更多的动态行为。下面是Java 8 Predicate 的例子,展示了过滤集合数据的多种常用方法。Predicate接口非常适用于做过滤。
代码块
public static void main(args[]){
List languages = Arrays.asList("Java", "Scala", "C++", "Haskell", "Lisp");
System.out.println("Languages which starts with J :");
filter(languages, (str)->str.startsWith("J"));
System.out.println("Languages which ends with a ");
filter(languages, (str)->str.endsWith("a"));
System.out.println("Print all languages :");
filter(languages, (str)->true);
System.out.println("Print no language : ");
filter(languages, (str)->false);
System.out.println("Print language whose length greater than 4:");
filter(languages, (str)->str.length() > 4);
}
public static void filter(List names, Predicate condition) {
for(String name: names) {
if(condition.test(name)) {
System.out.println(name + " ");
}
}
}
输出:
代码块
Languages which starts with J :
Java
Languages which ends with a
Java
Scala
Print all languages :
Java
Scala
C++
Haskell
Lisp
Print no language :
Print language whose length greater than 4:
Scala
Haskell
// 更好的办法
public static void filter(List names, Predicate condition) {
names.stream().filter((name) -> (condition.test(name))).forEach((name) -> {
System.out.println(name + " ");
});
}
可以看到,Stream API的过滤方法也接受一个Predicate,这意味着可以将我们定制的 filter() 方法替换成写在里面的内联代码,这就是lambda表达式的魔力。另外,Predicate接口也允许进行多重条件的测试,下个例子将要讲到
示例4:Java 8中使用lambda表达式的Map和Reduce示例
本例介绍最广为人知的函数式编程概念map。它允许你将对象进行转换。例如在本例中,我们将 costBeforeTax 列表的每个元素转换成为税后的值。我们将 x -> x*x lambda表达式传到 map() 方法,后者将其应用到流中的每一个元素。然后用 forEach() 将列表元素打印出来。使用流API的收集器类,可以得到所有含税的开销。有 toList() 这样的方法将 map 或任何其他操作的结果合并起来。由于收集器在流上做终端操作,因此之后便不能重用流了。你甚至可以用流API的 reduce() 方法将所有数字合成一个,下一个例子将会讲到。
代码块
// 不使用lambda表达式为每个订单加上12%的税
List costBeforeTax = Arrays.asList(100, 200, 300, 400, 500);
for (Integer cost : costBeforeTax) {
double price = cost + .12*cost;
System.out.println(price);
}
// 使用lambda表达式
List costBeforeTax = Arrays.asList(100, 200, 300, 400, 500);
costBeforeTax.stream().map((cost) -> cost + .12*cost).forEach(System.out::println);
在上个例子中,可以看到map将集合类(例如列表)元素进行转换的。还有一个 reduce() 函数可以将所有值合并成一个。Map和Reduce操作是函数式编程的核心操作,因为其功能,reduce 又被称为折叠操作。另外,reduce 并不是一个新的操作,你有可能已经在使用它。SQL中类似 sum()、avg() 或者 count() 的聚集函数,实际上就是 reduce 操作,因为它们接收多个值并返回一个值。流API定义的 reduceh() 函数可以接受lambda表达式,并对所有值进行合并。IntStream这样的类有类似 average()、count()、sum() 的内建方法来做 reduce 操作,也有mapToLong()、mapToDouble() 方法来做转换。这并不会限制你,你可以用内建方法,也可以自己定义。在这个Java 8的Map Reduce示例里,我们首先对所有价格应用 12% 的VAT,然后用 reduce() 方法计算总和。
代码块
// 为每个订单加上12%的税
// 老方法:
List costBeforeTax = Arrays.asList(100, 200, 300, 400, 500);
double total = 0;
for (Integer cost : costBeforeTax) {
double price = cost + .12*cost;
total = total + price;
}
System.out.println("Total : " + total);
// 新方法:
List costBeforeTax = Arrays.asList(100, 200, 300, 400, 500);
double bill = costBeforeTax.stream().map((cost) -> cost + .12*cost).reduce((sum, cost) -> sum + cost).get();
System.out.println("Total : " + bill);
示例5:通过过滤创建一个String列表
过滤是Java开发者在大规模集合上的一个常用操作,而现在使用lambda表达式和流API过滤大规模数据集合是惊人的简单。流提供了一个 filter() 方法,接受一个 Predicate 对象,即可以传入一个lambda表达式作为过滤逻辑。下面的例子是用lambda表达式过滤Java集合,将帮助理解。
1 2 3 |
// 创建一个字符串列表,每个字符串长度大于2 List System.out.printf("Original List : %s, filtered list : %s %n", strList, filtered); |
输出:
1 |
Original List : [abc, , bcd, , defg, jk], filtered list : [abc, bcd, defg] |
另外,关于 filter() 方法有个常见误解。在现实生活中,做过滤的时候,通常会丢弃部分,但使用filter()方法则是获得一个新的列表,且其每个元素符合过滤原则。
示例6:对列表的每个元素应用函数
我们通常需要对列表的每个元素使用某个函数,例如逐一乘以某个数、除以某个数或者做其它操作。这些操作都很适合用 map() 方法,可以将转换逻辑以lambda表达式的形式放在 map() 方法里,就可以对集合的各个元素进行转换了,如下所示。
代码块
// 将字符串换成大写并用逗号链接起来
List G7 = Arrays.asList("USA", "Japan", "France", "Germany", "Italy", "U.K.","Canada");
String G7Countries = G7.stream().map(x -> x.toUpperCase()).collect(Collectors.joining(", "));
System.out.println(G7Countries);
示例7:计算集合元素的最大值、最小值、总和以及平均值
IntStream、LongStream 和 DoubleStream 等流的类中,有个非常有用的方法叫做 summaryStatistics() 。可以返回 IntSummaryStatistics、LongSummaryStatistics 或者 DoubleSummaryStatistic s,描述流中元素的各种摘要数据。在本例中,我们用这个方法来计算列表的最大值和最小值。它也有 getSum() 和 getAverage() 方法来获得列表的所有元素的总和及平均值。
代码块
//获取数字的个数、最小值、最大值、总和以及平均值
List primes = Arrays.asList(2, 3, 5, 7, 11, 13, 17, 19, 23, 29);
IntSummaryStatistics stats = primes.stream().mapToInt((x) -> x).summaryStatistics();
System.out.println("Highest prime number in List : " + stats.getMax());
System.out.println("Lowest prime number in List : " + stats.getMin());
System.out.println("Sum of all prime numbers : " + stats.getSum());
System.out.println("Average of all prime numbers : " + stats.getAverage());
lambda部分引入:http://www.importnew.com/16436.html
===========================================================
博主花了几天时间把Java核心技术卷一所有内容基本阅读完毕,开始计划将本书学习内容分两部分总结起来。但是我发现,核心内容都是前6章,后面几章不是非常重要,我也就不准备继续总结了~(但是也有非常重要的内容如Java集合,这个后续我有时间也会继续跟进的)。后续的Java学习计划是,学习Java并发编程,但是在学习这个之前,我需要先学习JVM内存模型,欢迎大家继续关注啊~