[关闭]
@CLSChen 2019-08-21T01:33:34.000000Z 字数 36593 阅读 2109

Java基础

编程 JAVA


第一、二章 基本程序设计

java.包.类.对象.方法 java.util.Scanner.out.ptintln

  1. 变量在使用之前必须初始化
  2. System.out.println(x = 1)是正确的
    相当于
    int x = 1;
    System.out.println(x);
  3. final用来声明常量 常量名称大写final double PI = 3.14159
  4. 变量、方法全小写,多个单词则首字母大写——驼峰命名法
  5. 类首字母大写
  6. 常量所有字母大写 两个单词用下划线连接如 MAX_VALUE
  7. JAVA整数:byte、short、int、long。
    浮点数:float和double。后者是前者的两倍大小。
    通常情况下使用double,因为它比float精确。
  8. Scanner的方法 如nextByte() 读取一个byte类型的整数
    Scanner input = new Scanner(system.in)
  9. %用于除余数 如20%3=2 可以求负数 如-7%3=-1 7%-3=1
    偶数%2为0而奇数总为1可以用来判断奇偶
  10. -5的-号是一元操作符,4-5这里的-号是二元操作符
  11. Math.pow(a,b)可以计算a^b并返回结果,Math类隐藏在java.lang包中,此包为隐式导入的,因此,不需要在开始声明。
    System.out.println(Math.pow(2,3))
  12. 只要两者有一个浮点数结果就是浮点数5.0/2=2.5 5/2.0=2.5 5/2=2

整形字面值

  1. int 最大2147483648
  2. 若超过的整形字面值要在后面加l or L来示意这个数值是long类型 如2147483648L 同理1010.2d代表double 1002F代表float 这里不区分大小写
  3. 二进制0B 0B1111=15
  4. 八进制0 07777=4095
  5. 16进制0x 0XFFFF=65535
  6. 数字之间加_是被允许的 如124_123=124123

浮点型字面值

  1. double精确到小数点后16位 而float只到8位 因此double更加精确

科学计数法

  1. E即为*10 不区分大小写
  2. 123.456 = 1.23456E2 = 1.23456E+02

返回UNIX时间戳

  1. System.currentTimeMillis()可以返回从1970年1月1号到现在的毫秒数
  2. 通过毫秒数/1000可以得出秒数 秒数/60可以得出分钟数 分钟数/60为小时数 小时数/24为天数
  3. 通过秒数%60可以得出当前秒数 分钟数%60可以得出当前分钟数 小时数%24可以得出当前小时数

增强复制操作符

  1. += -= *= %= /=
  2. 中间没有空格!
  3. Java支持i++ i-- ++i --i
  4. int a = ++i; 先加了 返回新值
    int a = i++; 加了但返回旧值
  1. double x = 1.0;
  2. double y = 5.0;
  3. double z = x-- + (++y);
  4. x=0.0 y=6.0 z=7.0

5.从左到右对操作数求值 此规则高于一切

  1. int i = 1;
  2. int k = ++i + i*3
  3. i = 2, k =8
  4. int i = 1;
  5. int k = i++ + i*3
  6. i = 2, k = 7

6.应该避免在同一个表达式中多次使用该规则防止混乱

增强类型转换

  1. 将数值赋给范围更大的类型——扩展类型
  2. 将数值赋给范围更小的类型——缩小类型——必须显式
  3. (类型)数值 如(double)8为8.0
  4. 类型转换不改变被转换的变量 上面的8仍然是8 不会变成8.0
  1. int sum = 0;
  2. sum += 4.5;
  3. //sum += 4.5 等价于 sum = (int)(sum += 4.5)
  4. //该式子是正确的
  1. int i = 1;
  2. byte b = 1;//错误 必须使用显性的类型转换
  3. int i = 1;
  4. byte b = (byte)i;//正确
  1. (int)(tax * 100) / 100 //默认——将tax向下四舍五入保留小数点后两位
  2. (int)(x + 0.5) //将任意的tax向上四舍五入
  3. ==>
  4. (int)(x * 100 + 0.5)/100

软件开发过程

常见错误 P58

  1. int a = 1;
  2. double a = 1.0;
  3. //会报错重复变量

求一个整数各位数的和 P61 2.6题解答 哦也!

  1. package demo;
  2. import java.util.Scanner;
  3. public class Plus {
  4. public static void main(String[] args) {
  5. Scanner input = new Scanner(System.in);
  6. System.out.print("Enter a number between 0 and 1000:");
  7. int number = input.nextInt(); //输入一个三位数
  8. int units = number % 10; //932 % 10 = 2 并赋给个位
  9. number = number / 10; //932 / 10 = 93 修改number的值
  10. int tens = number % 10; //93 % 10 = 3
  11. number = number / 10; //93 / 10 = 9
  12. int huns = number;
  13. int plus = huns + tens + units;
  14. System.out.println(plus); //9+3+2=14
  15. }
  16. }

第三章 选择

  1. if() //if后面没有分号 有分号则等于后面有一个空的块 如if{}; 这是一个逻辑错误 很难被发现
  2. {}
  3. else if()
  4. {}
  5. else if()
  6. {}
  7. else
  8. {}

两个浮点数不能比较直接相等 可以用Math.abs()返回绝对值后定义一个极小的值来比较差距 如:

  1. final double EPSILON = 1E-14;
  2. double x = 1.0 - 0.1 - 0.1 - 0.1 - 0.1 - 0.1;
  3. if (Math.abs(x - 0.5) < EPSILON) //与0.5无限接近
  4. System.out.println(x + "is approximately 0.5");
  1. Math.random() //可以获得一个0.0到1.0之间的随机double值,不包括1.0
  2. (int)(Math.random() * 10 //因此可以获得一个0~9的整数

逻辑操作符

  1. ! //非
  2. && //与
  3. || //或
  4. ^ //异或

switch

  1. switch(year % 12){ //输入一个值 不可以是判断
  2. case 1:
  3. break;
  4. case 2:
  5. break;
  6. default: ; //默认语句
  7. }

1

条件操作符

max = (num1 > num2) ? num1 : num2;
若是括号里的为true,则执行结果为第一个代码块,若为false,则执行结果为第二个代码块。
右边的执行完毕后,再赋值给左边的max,起到取大的效果。

操作符优先级和结合规则

操作符优先级表

第四章 数学函数、字符和字符串

函数列表:P103 包括:

  1. a + Math.random() * b; //返回a ~ a+b之间的一个随机数,不包括a+b
  2. 15 + Math.random() * 30; //返回15~44的一个随机数

字符数据类型和操作

char用于表示单个字符,用单引号扩住。
而字符串字面值必须扩在双引号中。
因此,"A"是一个字符串,'A'是一个字符。

Unicode和ASCII码

字符 十进制编码值 Unicode值
'0' ~ '9' 48~57 \u0030~ \u0039
'A' ~ 'Z' 65~90 \u0041 ~ \u005A
'a' ~ 'z' 97~122 \u0061 ~ \u007A
  1. char letter = 'A';
  2. char letter = '\u0041';
  3. //这两行语句等价
  1. char ch = 'a';
  2. System.out. println(++ch);
  3. //将输出字符b

转义 P108

\t相当于Tab 输出四个空格
\n换行符
\"双引号
此处输入图片的描述

字符测试

此处输入图片的描述

*数值转换 P109

char型数据可以转换成任意一种数值类型,反之亦然,将整数转换成char类型只用到该数据的低16位。

  1. char ch = (char)0XAB0041;
  2. System.out.println(ch);
  3. //char为A
  4. char ch = (char)65; //0041(16)== 65(10)
  5. System.out.println(ch);
  6. //char为A
  7. //第一个char是16进制,将其转换的原理为先截取后四位,再转换为十进制值对应十进制编码值输出。
  8. //0041(16)== 65(10),因此上面两式子是等价的。
  9. char ch = (char)0041;
  10. System.out.println(ch);
  11. //char为!
  12. //如果直接截取后四位将其作为十进制数处理,结果将不同,是错误的。
  1. int i = (int)'A';
  2. //i is 65.

String 字符串

  1. String s = input.next() //以空格区分 字符串中不能有空格
  2. String s = input.nextLine(); //可以有空格 输入单个字符也用它
  1. int int1 = Integer.parseInt(intString);
  2. double double1 = Double.praseDouble(doubleString);
  1. String s = Integer.valueOf(23).toString();
  2. String s = Double.valueOf(23).toString();
  1. month == "Feb";
  2. //是错误的 它永远为false
  3. month.equal"Feb");
  4. //是正确的

此处输入图片的描述

格式化控制台输出printf

此处输入图片的描述

第五章 循环

三种循环

  1. while(){
  2. }
  3. ---
  4. do{
  5. }while(); //只有dowhile有分号
  6. ---
  7. for( ; ; ){
  8. }

无限循环错误

  1. int i = 1;
  2. while (i < 10)
  3. if(i % 2 ==0 )
  4. System.out.println(i);
  5. //无限循环错误 没有出现自增模块
  6. int i = 1;
  7. while (i < 10)
  8. if(i % 2 == 0)
  9. System.out.println(i++);
  10. //无限循环错误 自增模块在if里没机会得到执行
  11. int i = 1;
  12. while (i < 10)
  13. if((i++) % 2 == 0) //先以i=1判断 然后在下一句语句中得到自增
  14. System.out.println(i); //在i = 2,4,6,8 得到满足
  15. //输出i = 3,5,7,9

可以通过System.currentTimeMillis()计算测试所花的时间

  1. long startTime = System.currentTimeMillis();
  2. long endTime = System.currentTimeMillis();
  3. long testTime = endTime - startTime;
  4. System.out.println(testTime/1000);

不要比较浮点数是否相等来进行循环控制 因为浮点数是近似值

  1. double item = 1; double sum = 0;
  2. while(item != 0){
  3. sum += item;
  4. item -= 0.1;
  5. }//item永远不可能减到完全等于0,它会跳过0向负数进发,这将是一个无限循环。

输入重定向 可以将数据存在文件中来一次性输入大量的数据值

  1. java ClassName < input.txt
  2. //输入重定向,前面的是类名,将后面文件里面的数据导入类。
  3. java ClassName > output.txt
  4. //输出重定向,将类中产出的数据导入文件。
  5. java CLassName < input.txt > output.txt
  6. //输入输出重定向 先输入后输出 看起来就像把input文件括在里面

while和do-while循环的区别

do-while至少执行一次,某些情况下这两种会有一种更方便。
do-while改成while的话,某些语句需要在循环前和循环内重复出现以达到至少执行一次的效果。如:

while和for循环的区别

基本上都可以转换 但是for循环定义的变量是在内部,不会影响到后面的代码。
while循环定义的变量是会影响到之后的代码的。

嵌套循环

  1. for(int i = 0; i < 10; i++)
  2. for(int j = 0; j < i; j++)
  3. System.out.println(i * j);
  4. //该程序执行了多少次?
  5. //45次 = 1*1 + 2*2 + …9*9

最小化数值错误

在循环条件中使用浮点数将导致数值错误。
浮点数不够精确 可能会导致无限循环

  1. double item = 1; double sum = 0;
  2. while(item != 0){
  3. sum += item;
  4. item -= 0.1;
  5. }//item永远不可能减到完全等于0,它会跳过0向负数进发,这将是一个无限循环。
  6. for(double i = 0.01; i ,<= 1.0; i = i + 0.01)
  7. sum += i;
  8. // i可能最后会比1稍微大一点,而这将导致最后一次求和失败!数据偏差将会很大。

十进制转换16进制并且在最后通过用户标记控制循环!

  1. package demo;
  2. import java.util.Scanner;
  3. public class Dec2Hex {
  4. public static void main(String[] args) {
  5. char a = ' ';
  6. do {
  7. Scanner input = new Scanner(System.in);
  8. System.out.print("Please input a Dec:");
  9. int dec = input.nextInt(); //将十进制数存入dec变量
  10. String hex = " "; //定义一个空字符串
  11. do{
  12. int hexValue = dec % 16; //个位等于十进制数对16取余
  13. char hexDigit = (0 <= hexValue && hexValue <= 9) ?
  14. (char)(hexValue + '0') : (char)(hexValue - 10 + 'A');
  15. //定义一个字符,如果个位在0~9之间,则将其与'0'相加,
  16. //此时的答案是一个ASCII码,如45。
  17. //再通过char将其转化为字符,如'2'。
  18. //如果在10到16之间,则通过 - 10 + 'A'来计算。
  19. //如11 => 66 => B
  20. hex = hexDigit + hex;//将其插入空字符串的前面
  21. //注意:此处后面两个变量位置不能互换。
  22. dec = dec / 16;//新的十进制数由十进制数除以16得出
  23. }while(dec != 0);//直到dec/16为0为止,也就是dec<16为止
  24. //此时的dec就是16进制的最大位数上的数
  25. System.out.println("The hex number is:" + hex);
  26. System.out.print("Put Y to continue and N to exit:");
  27. a = input.next().charAt(0);
  28. //取一个字符串,并返回它的第一个字符给a。
  29. }while(a == 'Y');//如果a为'Y',则重新开始第一个循环。
  30. }
  31. }

break和continue 和if配合使用

break跳出整个循环
continue跳出一次循环 直接下一次

第六章 方法

方法定义
此处输入图片的描述

  1. package demo;
  2. import java.util.Scanner;
  3. public class Hex2Dec {
  4. public static void main(String[] args) {
  5. Scanner input = new Scanner(System.in);
  6. System.out.print("Enter a hex number: ");
  7. String hex = input.nextLine();
  8. System.out.println("The decimal value for hex number "
  9. + hex + " is " + hexToDecimal(hex.toUpperCase()));
  10. }
  11. public static int hexToDecimal(String hex){
  12. int decimalValue = 0;
  13. for (int i = 0; i < hex.length(); i++){
  14. char hexChar = hex.charAt(i);
  15. decimalValue = decimalValue * 16 + hexCharToDecimal(hexChar);
  16. }
  17. return decimalValue;
  18. }
  19. public static int hexCharToDecimal(char ch){
  20. if (ch >= 'A' && ch <= 'F')
  21. return 10 + ch - 'A';
  22. else
  23. return ch - '0';
  24. }
  25. }

输出一个数的反序数(ReverseNumber)

  1. package demo;
  2. import java.util.Scanner;
  3. public class ReverseNumber {
  4. public static void main(String[] args) {
  5. Scanner input = new Scanner(System.in);
  6. System.out.print("Please inter a int number: ");
  7. int number = input.nextInt();
  8. reverse(number);
  9. }
  10. public static void reverse(int number)
  11. {
  12. StringBuilder str = new StringBuilder(number + "");
  13. //利用StringBuilder方法来新建一个string字符串以方便修改
  14. int length = str.length();
  15. for(int j = 0,k = 1; j < length - 1; j++,k++) {
  16. //一共循环长度-1次,并定义k变量来每次排序后将排序位数减一
  17. for (int i = 0; i < length - k ; i++)
  18. { char a = str.charAt(i);
  19. char b = str.charAt(i + 1);
  20. str.setCharAt(i , b);//利用setCharAt方法
  21. str.setCharAt(i + 1 , a);//将提取出来的两个位置上的字符互换
  22. }
  23. }
  24. System.out.println("The inversion number is: "+ str);
  25. }
  26. }

第七章 一维数组

  1. double myList[] = new double[10];
  1. double[] myList = {1.9, 2.9, 3.4, 3.5};
  2. //这条语句声明、创建并且初始化包含4个元素的数组myList。
  3. //大括号!!!
  4. //该语句相当于
  5. double[] myList = new double[4];
  6. myList[0] = 1.9;
  7. myList[1] = 2.9;
  8. myList[2] = 3.4;
  9. myList[3] = 3.5;
  1. for(int i = 0; i < myList.length; i++)
  2. System.out.print(myList[i] + " ");
  3. //数值类型的数组需要for循环。
  4. char[] city = {'a','a'};
  5. System.out.print(city);
  6. //char类型的数组可以直接打印。

数组数值处理 P216

foreach循环

  1. for (double e : myList)
  2. System.out.print(e);
  3. //对myList中每个元素e进行以下操作,即遍历整个数组。
  4. //注意:变量e的数据类型必须与数组类型相同。

复制数组

  1. 利用for循环逐个复制
  2. 利用静态方法arraycopy(注意此方法copy的c没有大写)
  1. arraycopy(原数组,原数组起始位置,目标数组,目标数组起始位置,元素个数)
  2. System.arraycopy(sourceArray, 0, targetArray, 0, sourceArray.length);
  3. System.arraycopy(source, 0, t, 0, source.length);
  4. //用此方法前要先声明目标数组并分配内存空间。

数组传递给方法后值会改变

  1. swap(a[0], a[1])
  2. public static void swap(int a1, int a2){}
  3. //这样改变的只是实参,而不会涉及数组。 ×
  4. swap(a)
  5. public static void swap(int[] array){}
  6. //这样传递的是整个数组。 √

可变长参数列表

  1. public static viod printMax(doublenumbers) {
  2. if (numbers.length == 0)
  3. System.out.println("No argument passed");
  4. return;
  5. }

二分查找法 需要先从小到大排好序

  1. package demo;
  2. public class BinarySearch {
  3. //二分查找法(不含main函数)
  4. public static int binarySearch(int[] list, int key) {
  5. int low = 0;
  6. int high = list.length - 1;
  7. while (high >= low) { //此处不能用 > ,不然对只有一个元素的数组无法处理。
  8. int mid = (low + high) / 2;
  9. if (key < list[mid])
  10. high = mid - 1;
  11. else if (key == list[mid])
  12. return mid;
  13. else {
  14. low = mid + 1;
  15. }
  16. }
  17. return -low-1;
  18. }
  19. }

选择排序算法

  1. package demo;
  2. import java.util.Scanner;
  3. public class SelectSort {
  4. //选择排序算法:从所有数中选择最小的与第一个交换。
  5. public static void main(String[] args) {
  6. Scanner input = new Scanner(System.in);
  7. int[] list = { 5, 2, 3, 7, 9, 8, 1 };
  8. for (int j = 0; j < list.length - 1; j++) {//用j < list.length 也可运行,但是要多运算一步。
  9. int min = list[j];//将第一个值赋给min,后面的值与其比较。
  10. int tag = j;
  11. for (int i = j + 1; i < list.length; i++) {//用i = j也可运行,但是要多运算一步。
  12. if (list[i] < min) {
  13. min = list[i];//记录最小值。
  14. tag = i;//标记最小下标。
  15. }
  16. }
  17. list[tag] = list[j];
  18. list[j] = min;//将当前最前面的值于min所处位置的值交换。
  19. }
  20. for (int e : list)
  21. System.out.println(e);//for each循环输出。
  22. }
  23. }

Arrays类

  1. Arrays.sort(list, indexStart, indexEnd);//从小到大对start和end里面进行排序,不包括end。
  2. Arrays.parallelSort();//同上,但是多处理器这个会快些。
  3. Arrays.binarySearch();//二分查找法,必须提前按升序排好。
  4. Arrays.equals(list1, list2);//严格比较相等,一对一。
  5. Arrays.fill(list1, 5);//使用5填充整个数组。
  6. Arrays.fill(list1, 1, 5, 8);//使用8填充1至5地址,不包括5。
  7. Arrays.toString(list);//将数组返回成一个字符串。

向main方法传递参数

main就像一个普通的函数,它可以接收字符串args。
不仅可以直接在其他类调用它,还可以从命令行向他传递参数。

第八章 多维数组

多维数组的定义

  1. int[][] matrix;
  2. matrix = new int[5][11];
  3. //两句可以合并成一句:
  4. int[][] matrix = new int[5][12];
  1. matrix[2][14] = 7; //是正确的
  2. matrix[2,7] = 7; //是错误的
  1. array.length//行数
  2. array[0].length//第一行的长度

二维数组初始化

  1. int[][] array ={
  2. {1,2,3},
  3. {1,2,3},
  4. }
  5. int[][] array2 ={
  6. {1,2,3},
  7. {1,2},
  8. }//不规则数组是被允许的
  9. 如果不想一开始就给不规则数组赋值 可以使用空括号
  10. int[][] array = new int[5][];
  11. //第一个括号必须有值。

处理二维数组 P251

将二维数组传递给方法

  1. package demo;
  2. import java.util.Scanner;
  3. public class Test {
  4. public static void main(String[] args) {
  5. int[][] m = getArray();
  6. System.out.println("\nSum of all elements is " + sum(m));
  7. }
  8. // 返回一个二维数组
  9. public static int[][] getArray() {
  10. Scanner input = new Scanner(System.in);
  11. int[][] m = new int[3][15];
  12. System.out.println("Enter " + m.length + " rows and " + m[0].length + " columns: ");
  13. for (int i = 0; i < m.length; i++)
  14. for (int j = 0; j < m[i].length; j++)
  15. m[i][j] = input.nextInt();
  16. return m;
  17. }
  18. // 对一个二维数组求和
  19. public static int sum(int[][] m) {
  20. int total = 0;
  21. for (int i = 0; i < m.length; i++) {
  22. for (int j = 0; j < m[i].length; j++)
  23. total += m[i][j];
  24. }
  25. return total;
  26. }
  27. }
  28. //for(int e :list)
  29. //System.out.print(e);

矩阵乘法!哦也!

  1. package demo2;
  2. import java.util.Scanner;
  3. public class MultiplyMatrix {
  4. //矩阵乘法
  5. public static void main(String[] args) {
  6. Scanner input = new Scanner(System.in);
  7. System.out.print("请输入第一个矩阵的行数和列数,如(2 3):");
  8. int arow = input.nextInt();
  9. int acol = input.nextInt();
  10. int[][] a = new int[arow][acol];
  11. System.out.print("请输入第二个矩阵的行数和列数,如(2 3):");
  12. int brow = input.nextInt();
  13. int bcol = input.nextInt();
  14. int[][] b = new int[brow][bcol];
  15. if (arow != bcol) {
  16. System.out.print("Input invalid.");
  17. System.exit(0);
  18. }
  19. System.out.println("请输入第一个矩阵的数值:");
  20. for (int i = 0; i < a.length; i++) {
  21. for (int j = 0; j < a[0].length; j++) {
  22. a[i][j] = input.nextInt();
  23. }
  24. }
  25. System.out.println("请输入第二个矩阵的数值:");
  26. for (int i = 0; i < b.length; i++) {
  27. for (int j = 0; j < b[0].length; j++) {
  28. b[i][j] = input.nextInt();
  29. }
  30. }
  31. int[][] c = multiplyMatrix(a, b);
  32. for (int i = 0; i < c.length; i++) {
  33. for (int j = 0; j < c[0].length; j++) {
  34. System.out.print(c[i][j] + " ");
  35. }
  36. System.out.println();
  37. }
  38. }
  39. public static int[][] multiplyMatrix(int[][] a, int[][] b) {
  40. if (a.length != b[0].length) {
  41. System.out.print("The input is invalid.");
  42. return null;
  43. }
  44. int[][] c = new int[a.length][b[0].length];
  45. int length = a.length;
  46. for (int i = 0; i < length; i++) {
  47. for (int j = 0; j < length; j++) {
  48. for (int j2 = 0; j2 < a[0].length; j2++) {
  49. c[i][j] += a[i][j2] * b[j2][j];
  50. }
  51. }
  52. }
  53. return c;
  54. }
  55. }

第九章 对象和类

UML类图详解:https://www.cnblogs.com/pangjianxin/p/7877868.html


Date类和Random类、Print2D类

此处输入图片的描述

静态方法和静态数据 static

  1. static int getNumberOfObjects() {
  2. return numberOfObjects;
  3. }
  4. 可以直接通过类名调用而不用声明对象
  5. Circle.getnumberOfObjects;

可见性修饰符

对象数组

  1. Circle[] cir = new Circle[5];
  2. for (i = 0; i < Circle.length; i++) //创建一个新数组。
  3. cir[i] = new Circle(); //为每一个成员初始化。

不可变对象

  1. public class Student{
  2. private java.util.date dateCreated;
  3. public java.util.Date getDateCreated{
  4. return dateCreated;
  5. }
  6. }
  7. public class Test {
  8. public static void main(String[] args){
  9. Student student = new Student(m223333, "John");
  10. java.util.Date dateCreated2 = student.getDateCreated();
  11. // 传出了dateCreated对象的引用
  12. // dateCreated2和dateCreated其实指向的是同一个对象
  13. dateCreated2.setTime(200000);
  14. // 达到修改对象的目的

因此要创建一个不可变对象,要满足三点:

  1. 所有数据都是私有的。
  2. 没有set方法。
  3. 没有一个返回指向可变数据域的引用的访问器方法。

变量的作用域

this引用

https://blog.csdn.net/Return_head/article/details/80744056

1.假如在一个构造方法中使用了this语句,那么它必须作为构造方法的第一条语句(不考虑注释语句)。
2.只能在一个构造方法中使用this语句来调用类的其他构造方法,而不能在实例方法中用this语句来调用类的其他构造方法。
3.只能用this语句来调用其他构造方法,而不能通过方法名来直接调用构造方法。

第十章 面向对象思考

将基本数据类型值作为对象处理——包装类 P349

此处输入图片的描述

BigInteger and BigDecimal

  1. 初始化:BigInteger sum=new BigInteger("0");
  2. 加法:sum=sum.add(n1); //这里sum和n都为BigInteger类型
  3. 减法:sum=sum.subtract(n1);
  4. 乘法:sum=sum.multiply(n1);
  5. 除法:sum=sum.divide(n1);
  6. 幂运算:sum=sum.pow(10);
  7. 取相反数:sum=sum.negate();
  1. 初始化:BigDecimal num1=new BigDecimal("1234.56453324");
  2. 加法:sum=sum.add(n1); //这里sum和n都为BigDecimal类型
  3. 减法:sum=sum.subtract(n1);
  4. 乘法:sum=sum.multiply(n1);
  5. 除法:sum=sum.divide(n1);
  6. BigDecimal类型转换为double类型:num1.doubleValue();
  7. 比较大小:num1.compareTo(num2);
  8. //小于时,返回-1; 等于时,返回0; 大于时,返回1。
  9. 四舍五入处理:
  10. num1.divide(num2,scale,BigDecimal.ROUND_HALF_UP).doubleValue();
  11. //scale表示保留几位小数

String类

String类源码:
https://blog.csdn.net/ylyg050518/article/details/52352993

构造字符串

  1. String message = new String("hello");
  2. // 一般用法
  3. String message = "hello";
  4. // Java将字符串直接看做String对象,这样也合法。

不可变字符串与限定字符串

  1. String s = "Java";
  2. s = "HTML": ;
  3. // "Java"对象被抛弃,s引用为新的string对象"HTML"。
  1. String s1 = "Welcome to ]ava";
  2. String s2 = new String("Welcome to ]ava");
  3. String s3 = "Welcome to ]ava";
  4. //s1 == s3, s1 != s2;

字符串的替换和分隔

可以使用

  1. str.replace('a','b');
  2. str.replaceAll("ab","abc");
  1. String[] tokens = "Java#HTML#Perl".split("#");
  2. for (int i = 0; i < tokens.length; i++)
  3. System.out.print(tokens[i] + " ");
  4. // split将字符串分割成了三个小字符串并存入数组。
  5. //显示Java HTML Perl

正则表达式

  1. "Java".matches("Java");
  2. "Java is fun".matches("Java.*");
  3. "Java is cool".matches("Java.*");
  4. //字符串.*与零个或多个字符相匹配。
  5. // . 任意字符 *任意长度
  6. String s = "a$b%c^".replaceAll("[$%^]","NNN");
  7. System.out.println(s);
  8. //aNNNbNNNcNNN
  9. String[] tokens - "Java.C?C#,C++".split("[.,:;?]");
  10. for (int i = 0; i < tokens.length; i++) System.out.println(tokens[i]+" ");
  11. //Java C C# C++

字符串与字符数组的相互转换

  1. char[] a = "Java".toCharArray();
  2. char[] dst = {'J','A','V','A','1','3','0','1'};
  3. "CS3720".getChars(2,6,dst,4);
  4. //dst{'J','A','V','A','3','7','2','0'};
  1. String str = new String(new char[]{'a','a'});
  2. // 可以直接使用构造方法。
  3. String str = String.valueOf(new char[]{'a','a'});
  4. // 也可以使用valueOf方法。

字符串和数值的相互转换

  1. Double.parseDouble(str);
  2. Integer.parseInt(str);
  1. String a = String.valueOf(5.44);
  2. //a:{'5','.','4','4'}

格式化字符串

  1. String s = String.format("%7.2f%6d%-4s", 45.556, -4s, "AB");
  2. System.out.println(s)
  3. //□□45.56□□□□14AB□□
  4. 注意
  5. System.out.printf();
  6. 等价为
  7. System.out.print(String.format());

StringBuilder And StringBuffer Class

此处输入图片的描述

append重载方法

  1. StringBuilder StringBuilder = new StringBuilder();
  2. stringBuilder.append("Welcome");
  3. stringBui1der.append(' ');
  4. stringBuilder.append("to");
  5. stringBui1der.append(' ');
  6. stringBui1der.append("Java");
  7. //"Welcome to Java"

insert、delete、deleteCharAt、reverse、replace、setCharAt重载方法

  1. //"Welcome to Java"
  2. StringBuilder.insert(11, "HTML and ");
  3. //"Welcome to Html and Java"
  1. stringBui1der.delete(8,11) // Welcome Java。
  2. stringBuilder.deleteCharAt(8) // Welcome o Java。
  3. stringBuilder.reverse() // avaJ ot emocleW。
  4. stringBuilder.replace(11,15,"HTML") // Welcome to HTML。
  5. stringBuilder.setCharAt(0,'w') // welcome to Java

toString、capacity、length、setLength和charAt构建器和返回方法

此处输入图片的描述

去除字符串中除了数值和字母的字符并判断是否为回文

  1. package demo2;
  2. import java.util.Scanner;
  3. public class PalindromelgnoreNonAlphanumeric {
  4. public static void main(String[] args) {
  5. Scanner input = new Scanner(System.in);
  6. System.out.print("input a string: ");
  7. String s = input.nextLine();
  8. System.out.print(s + " " + isPalindrome(s));
  9. }
  10. public static boolean isPalindrome(String s) {
  11. StringBuilder s1 = new StringBuilder();
  12. //将字符串转换为stringbuilder对象
  13. for(int i = 0; i<s.length();i++) {
  14. if(Character.isLetterOrDigit(s.charAt(i))) {
  15. //使用character类中的方法来判断是否为数字和字母
  16. //也可以使用编码。
  17. s1.append(s.charAt(i));
  18. }
  19. }
  20. StringBuilder s2 = s1;
  21. s2 = s2.reverse();
  22. return s1.equals(s2);
  23. }
  24. }

String类的实现 oh yeah!

  1. package demo2;
  2. public class MyString1 {
  3. private final char value[];
  4. public static void main(String[] args) {
  5. // char[] a = "Hello World".toCharArray();
  6. // MyString1 s = new MyString1(a);
  7. // char[] b = "Hello World".toCharArray();
  8. // MyString1 s2 = new MyString1(b);
  9. System.out.print(MyString1.valueOf(123455).value);
  10. }
  11. public MyString1(char[] chars) {
  12. value = chars;
  13. //源码中用copy实现
  14. }
  15. public MyString1() {
  16. this.value = new char[0];
  17. }
  18. public char charAt(int index) {
  19. return value[index];
  20. }
  21. public int length() {
  22. return value.length;
  23. }
  24. public MyString1 substring(int begin, int end) {
  25. char[] newValue = new char[end - begin];
  26. for (int i = begin, j = 0; i < end; i++, j++) {
  27. newValue[j] = value[i];
  28. }
  29. MyString1 s1 = new MyString1(newValue);
  30. return s1;
  31. }
  32. public MyString1 toLowerCase() {
  33. MyString1 s1 = new MyString1(value);
  34. for (int i = 0; i < s1.value.length; i++) {
  35. if (s1.value[i] >= 'A' && s1.value[i] <= 'Z') {
  36. s1.value[i] = (char) (s1.value[i] - 'A' + 'a');
  37. }
  38. }
  39. return s1;
  40. }
  41. public boolean equals(MyString1 s) {
  42. boolean isequal = true;
  43. if (s.length() != value.length) {
  44. return false;
  45. }
  46. for (int i = 0; i < s.length(); i++) {
  47. if (s.value[i] != value[i])
  48. isequal = false;
  49. }
  50. return isequal;
  51. }
  52. public static MyString1 valueOf(int i) {
  53. int digit = 1;
  54. int k = i;
  55. while (k / 10 > 0) {
  56. digit++;
  57. k = k / 10;
  58. }
  59. char[] ch = new char[digit];
  60. for (int j = digit - 1; j >= 0; j--, i = i / 10) {
  61. if (j == 0) {
  62. ch[j] = (char)(i + 48);
  63. } else {
  64. ch[j] = (char)((i % 10) + 48);
  65. }
  66. }
  67. return new MyString1(ch);
  68. }
  69. }

第11章 继承和多态

继承

  1. public class Circle extends CeometricObject
  2. // Subclass 继承于 Superclass

使用super关键字

  1. 普通函数
  2. super.toString();
  3. Superclass.toString();
  4. // 等价
  5. 构造函数
  6. super();
  7. // 正确
  8. Superclass();
  9. // 错误!在子类中不能直接用函数名调用父类的构造函数,只能使用super。
  1. public ClassName(double a){
  2. //some statements
  3. }
  4. 等价于
  5. public ClassName(double a){
  6. super();
  7. //some statements
  8. }

因此,如果父类没有无参构造方法,会出现一个错误。

问题:子类继承了父类的普通方法,那为什么还要用super调用父类的普通方法?

方法重写

POINT:
要重写一个方法,需要在子类中使用和父类一样的签名以及一样的返回值类型来对该方法进行定义。

重写标注:@Override

Object类与toString( )方法和equals()方法

Java中的所有类都继承自java.lang.Object类。

  1. object1.equals(object2);

默认的equals只能判断两个是否指向同一对象。

注意,在object类中,equals的参数是object类,因此重写的时候要遵循这个规则。

  1. public boolean equals(Circle o){
  2. } //是错误的
  3. public boolean equals(Object o){
  4. } //是正确的

例子:

  1. public class Text {
  2. public static void main(String[] args) {
  3. Object a = new Circle();
  4. Object b = new Circle();
  5. System.out.print(a.equals(b));
  6. }
  7. 1.
  8. public class Circle {
  9. double radius;
  10. public boolean equals(Object circle) {
  11. return this.radius == ((Circle) circle).radius;
  12. }
  13. }// 输出为true。两者的半径相同。
  14. 2.
  15. public class Circle {
  16. double radius;
  17. public boolean equals(Circle circle) {
  18. return this.radius == circle.radius;
  19. }
  20. }// 输出为false。它根本就没有调用Circle里的这个euqals方法,因为传入的值是object类型,它直接调用了Object类中的equals去了!因为比较的是引用,两者没有指向同一个变量,所以为false。

多态

面向对象程序设计的三大支柱是封装、继承和多态。

动态绑定

方法可以在沿着继承链的多个类中实现。JVM决定运行时调用哪个方法。

  1. Object o = new GeometricObject();
  2. //声明类型 实际类型

对象转换和instanceof运算符

对象转换和强制类型转换很像。可以将子类隐式转换为父类,但父类下来必须用强制转换。就像byte可以直接转换为int,但是int赋值给byte而不用(int)byte就会出错。

  1. Object o = new Student;
  2. // 可以将一个Student对象的引用赋给object变量
  3. Student b = o
  4. // 错误!原因是Student对象总是Object的实例,但是,Object对象不一定是 Student的实例。
  5. // 即使可以看到o实际上是一个Student的对象,但是编译器还没有聪明到知道这一点。
  6. Student b = (Student)o;

转换记住左大右小。

  1. Object a = new Circle();
  2. if(a instanceof Circle){
  3. // *使用instanceof关键字*来确定这个实例是不是这个类的实例。
  4. System.out.print(((Circle)a).getYou());
  5. }
  6. // 然后再将Object变量的*引用强制转换*为对Circle对象的引用,才能调用Circle中的getYou方法。

即使需要进行如此复杂的对象类型转换,一开始也要定义为父类(Object),这样,它就能接受任意子类型的值。

ArrayList类

可以创建一个数组存储对象,但是这个数组一旦创建,它的大小就固定了。
因此,Java提供ArrayList类来存储不限定个数的对象。
即可变对象列表(不叫数组)。

微信截图_20190326141224.png-1054.3kB

  1. import java.util.ArrayList
  1. ArrayList<Class> a = new ArrayList<Class>();
  2. JDK1.7后也可以简写为
  3. ArrayList<Class> a = new ArrayList<>();

ArrayList虽然和数组很像,但是还是有不同之处。
微信截图_20190326142123.jpg-434.6kB

ArrayList和数组可以相互转换。

  1. 将数组转换为列表
  2. String[] a = {"a","b","c"};
  3. ArrayList<String> list = new ArrayList<>(Arrays.asList(a));
  4. // 使用Arrays类中的静态方法asList()。
  5. 将列表转换为数组
  6. String[] b = new String[list.size()];
  7. list.toArray(b);
  8. // ArrayList类中的普通方法toArray()将list中的元素复制到数组内。

java.util.Collections类中有一些有用的方法。如:排序,最大值,随机打乱。

  1. import java.util.Collections
  2. Intege [] array = {3, 5, 95, 4, 15, 34, 3, 6, 5};
  3. ArrayList<Integer> list = new ArrayList<>(Arrays.asList(array));
  4. Collections.sort(list);
  5. // 排序
  6. Collections.max(list);
  7. Collections.min(list);
  8. Collections.shuffle(1ist);
  9. // 随机打乱

protected数据和方法

一个类中的受保护成员可以从子类中访问。
在UML类图中,protected的方法前面加#

微信截图_20190326152939.png-192.2kB

例子:
微信截图_20190326153730.jpg-484kB

可以看出,protected只比默认开放一点点,只是增加了在继承类中可以访问,而在不同包里仍不可以访问。而同一个包里不管继不继承,只要不是private就可以访问

第12章 异常处理和文本I/O

异常是从方法抛出的。方法的调用者可以捕获以及处理该异常。
异常需要消耗系统资源,因此,可以预料的错误尽量使用if。
并且,不要将异常处理当做简单的逻辑测试。

  1. public static int quotient(int num1,int num2){
  2. if (num2 == 0){
  3. throw new ArithmeticException("Divisor can not be Zero. ")
  4. }
  5. return num1 / num2;
  6. }
  7. public static void main(String[] args){
  8. try{
  9. int result = quotient(1, 0);
  10. System.out.print(result);
  11. } //如果遇到异常,则抛出一个异常对象,并被catch捕获。
  12. catch(ArithmeticException ex){
  13. //程序跳转至catch功能块,并在处理完catch块后接着往下运行。
  14. System.out.print("Invaild input. ")
  15. }
  16. }

image.png-228.8kB

微信截图_20190326210808.png-969.9kB

声明异常,抛出异常和捕获异常

  1. public void method() throws IOException
  2. //多个异常
  3. public void method()
  4. throws IOException,Exception2,Exception3...ExceptionN

  1. IllegalArgumentException ex = new IllegalArgumentException("Wrong Argument");
  2. throw ex;
  3. //可以使用简写
  4. throw new IllegalArgumentException("Wrong Argument");
  1. 注意:声明异常的关键字是throws,抛出异常的关键字是throw
  2. 异常类包含一个无参构造方法和带字符串的构造方法。可以用getMessage()获取。

  1. try{
  2. statements;
  3. }
  4. catch (Exception1 exVarl){
  5. handler for exceptionl;
  6. }
  7. catch (Exception2 exVar2){
  8. handler for exception2;
  9. }
  10. ..
  11. catch (ExceptionN exVarN){
  12. throw exVarN;// Java允许重新抛出异常。
  13. }
  14. //JDK7的新的多捕获特征可以用来简写
  15. catch(Exception1 | Exception2 | ... | ExceptionN){
  16. //same handling these exceptions
  17. }
  1. 一个通用的父异常类可以衍生出各种子类,因此,如果父类可以被catch捕获,他的所有子类都会被捕获。
  2. 因此,不能将父类的catch块放到子类之前,编译将不会通过。

从异常中获取信息

微信截图_20190401170114.png-304.4kB

finally子句

  1. try{
  2. statements;
  3. }
  4. catch(TheException ex){
  5. handling ex;
  6. }
  7. finally{
  8. finalStatements;
  9. }

链式异常

和其他异常一起抛出一个异常,形成了链式异常。

  1. catch(Exception ex){
  2. throw new Exception("new error", ex);
  3. }

创建自定义异常类 P426

可以通过派生java.lang.Exception类来定义一个自定义异常类。

  1. public class InvalidRadiusException extends Exception {
  2. private double radius;
  3. public InvalidRadiusException(double radius) {
  4. super("Invalid radius " + radius);
  5. //调用父异常类Exception的构造方法。
  6. this.radius = radius;
  7. }
  8. public double getRadius() {
  9. return radius;
  10. }
  11. }

File类 文件处理

java.io.File类包含了获得一个文件/目录的属性,以及对文件/目录进行改名和删除的方法。


  1. c:\book\Welcome.java

绝对文件名是依赖机器的,
而相对文件名是相对于当前工作目录的。

  1. Welcome.java
  2. 如果当前工作目录是c:\book
  3. 绝对文件名: c:\book\Welcome.java

  1. Windows" \ "
  2. // Java中,反斜杠要写成"\\"
  3. new File("c:\\book\\test.dat")
  1. Unix: " / "
  2. // Java支持直接使用"/"
  3. new File("c:/book/test.dat")

image.png-1216kB

文件输入和输出(文本文件)

使用Scanner类从文件中读取文本数据,使用PrintWriter类向文本文件中写入数据。

PrintWriter类

  1. PrintWriter output = new PrintWriter(filename);

image.png-823.9kB

注意:PrintWriter可以直接使用File类的对象作为构造方法的参数。
也就是说它可以以指定文件名创建对象,也可以直接利用现成的File类对象。
调用PrintWriter类构造方法将直接新建一个文件。

  1. public class WriteData {
  2. public static void main(String[] args) throws IOException{
  3. //使用File类来检测scores.txt是否已经存在
  4. java.io.File file = new java.io.File("scores.txt");
  5. if(file.exists()){
  6. System.out.println("File already exists");
  7. System.exit(1);
  8. }
  9. //直接新建一个文件,利用File的对象file
  10. java.io.PrintWriter output = new java.io.PrintWriter(file);
  11. //调用实例方法来写入文件
  12. output.print("John T Smith ");
  13. output.println(90);
  14. output.print("Eric K Jones ");
  15. output.println(85);
  16. //不要忘记关闭文件
  17. output.close();
  18. }
  19. }

你知道的,程序员总是会忘记关闭文件。所以我们可以使用try-with-resources语法来自动关闭资源。


  1. try(声明和创建资源)
  2. {
  3. 处理文件;
  4. }

注意:try后面跟着的是小括号()

  1. public class WriteData {
  2. public static void main(String[] args) throws IOException{
  3. //使用File类来检测scores.txt是否已经存在
  4. java.io.File file = new java.io.File("scores.txt");
  5. if(file.exists()){
  6. System.out.println("File already exists");
  7. System.exit(1);
  8. }
  9. //在try的括号中声明并新建
  10. try(
  11. java.io.PrintWriter output = new java.io.PrintWriter(file);
  12. ){
  13. //调用实例方法来写入文件
  14. output.print("John T Smith ");
  15. output.println(90);
  16. output.print("Eric K Jones ");
  17. output.println(85);
  18. }// 不需要用到close方法!
  19. }
  20. }

Scanner类

  1. Scanner input = new Scanner(new File(filename));

image.png-749.3kB

注意:new Scanner(String)为给定的字符串创建一个Scanner对象。
等价于:从给定的字符串读取数据。
当要从文件读取数据时,要先用new File(filename)创建一个File对象,
然后用new Scanner(filename)来从文件读取数据。

  1. scores.txt
  2. {
  3. John T Smith 90
  4. Eric S Cgjlj 99
  5. }
  6. public class ReadData {
  7. public static void main(String[] args) throws IOException{
  8. java.io.File file = new java.io.File("scores.txt");
  9. try(
  10. Scanner input = new Scanner(file);
  11. ){
  12. while(input.hasNext()){
  13. //每次迭代(到没有标记为止)都从文本文件中读取名字、中间名、姓和分数
  14. String firstname = input.next();
  15. String mi = input.next();
  16. String lastname = input.next();
  17. int Score = input.nextInt();
  18. System.out.println(firstName + " " + mi + " " + lastName + " " + Score);
  19. }
  20. }
  21. }
  22. }

从Web读取数据

java.net.URL类可以用来读取web的文件。
注意:URL全大写。


  1. public URL(String spec)
  2. throws MalformedURLException
  1. try{
  2. URL url = new URL("https:/www.google.com/index.html")
  3. }
  4. catch (MalformedURLException ex){
  5. ex.printStackTrace();
  6. }

  1. URL url = new URL("https:/www.google.com/index.html")
  2. Scanner input = new Scanner(url.openStream());
  1. import java.util.Scanner;
  2. import java.net.URL;
  3. public class ReadFileFromURL {
  4. public static void main(String[] args){
  5. System.out.print("Enter a URL: ");
  6. String URLString = new Scanner(System.in).next();
  7. // 读取URL内容字符串
  8. try {
  9. URL url = new URL(URLString);
  10. // 以URL内容字符串构造URL对象
  11. int count = 0;
  12. Scanner input = new Scanner(url.openStream());
  13. // 开放url数据流以读取其中数据。
  14. while(input.hasNext()){
  15. String line = input.nextLine();
  16. count += line.length();
  17. // 读取每个字符串的长度并相加。
  18. }
  19. System.out.println("The file size is " + count
  20. + " characters");
  21. // 输出文件长度
  22. }
  23. catch (java.net.MalformedURLException ex)ex {
  24. System.out.println("Invaild URl");
  25. }
  26. catch (java.io.IOException ex){
  27. System.out.println("I/O Errors: no such file");
  28. }
  29. }
  30. }

第13章 抽象类和接口

父类定义了相关子类中的共同行为,接口可以用于定义类的共同行为。

抽象类

  1. // 抽象类
  2. public abstract class Geo{}
  3. // 抽象方法
  4. public abstract double getArea();
  5. public abstract double getPerimeter();

微信截图_20190403161620.png-1255.7kB

  1. Geo[] objects = new Geo[10];
  2. // 然后可以创造一个Geo子类的实例,并将它的引用赋值给数组。
  3. objects[0] = new Circle();

接口


  1. public interface T{
  2. int K = 1;
  3. void p();
  4. }
  5. // 等价于
  6. public interface T{
  7. public static final int K = 1;
  8. public abstract void p();
  9. }
  1. class Chicken extends Animal implements Edible{}
  2. // 在子类中必须实现抽象类和接口中的所有抽象方法。
  3. @Override
  4. public String howToEat(){
  5. return "Chicken: Fry it.";
  6. }
  7. @Override
  8. public String sound(){
  9. return "Chicken: cock-a-doodle-doo";
  10. }

Comparable接口

Comparable接口定义了compareTo对象,用来比较对象。

  1. 基础类型的封装类,BigIntegerBigDecimalCalendarStringDate等。

Cloneable接口

Cloneable接口给出了一个可克隆的对象。

浅复制和深复制


接口与抽象类

一个类可以实现多个接口,但是只能继承一个父类。
其他区别:

image.png-212.3kB

  1. public class NewClass extends BasicClass implements
  2. Interface1,Interface2...InterfaceN
  1. public interface Interface1 extends Interface2,Interface3..

注意,继承了子接口的类要实现其上所有类中的构造方法。


类的设计原则


第17章 二进制 I/O

二进制IO根类

微信截图_20190408165638.png-986.5kB
微信截图_20190408165648.png-592.3kB


FileInputStream类和FileOutputStream类

  1. // 利用File类来新建对象
  2. FileInputStream input = new FileInputStream(new File("abc.dat"));
  3. // 也可以直接使用文件名来新建对象
  4. FileInputStream input = new FileInputStream("abc.dat");
  5. 如果对象名的文件没存在,则创建这个文件。如果对象名的文件存在,那么将会清除文件的内容。如果不想清空内容,可以使用另一个构造方法。
  6. // 在后面增加一个true来在文件中增添内容
  7. FileInputStream input = new FileInputStream("abc.dat", true);

FilterInputStream类和旗下的DataInputStream与BufferedInputStream

  1. DataInputStream input =
  2. new DataInputStream(new FileInputStream("in.dat"));
  3. DataOutputStream Output =
  4. new DataOutputStream(new FileOutputStream("out.dat"));

微信截图_20190408223343.jpg-409.5kB

  1. import java.io.*;
  2. public class TestDataStream {
  3. public static void main(String[] args) throws IOException{
  4. try(
  5. DataOutputStream output = new DataOutputStream(new FileOutputStream("temp.dat"));
  6. ){
  7. output.writeUTF("John");
  8. output.writeDouble(85.5);
  9. output.writeUTF("Jim");
  10. output.writeDouble(185.5);
  11. output.writeUTF("George");
  12. output.writeDouble(105.25);
  13. }
  14. try(
  15. DataInputStream input = new DataInputStream(new FileInputStream("temp.dat"));
  16. ){
  17. System.out.println(input.readUTF() + " " +input.readDouble());
  18. System.out.println(input.readUTF() + " " +input.readDouble());
  19. System.out.println(input.readUTF() + " " +input.readDouble());
  20. }
  21. }
  22. }
  1. DataInputStream input =
  2. new DataInputStream(new BufferedInputStream(new FileInputStream("fuck.dat")));

第18章 递归

递归是程序的一种替换形式,实际上就是不用循环控制的重复。

百度百科:
程序调用自身的编程技巧称为递归( recursion)。递归做为一种算法在程序设计语言中广泛应用。一个过程或函数在其定义或说明中有直接或间接调用自身的一种方法,它通常把一个大型复杂的问题层层转化为一个与原问题相似的规模较小的问题来求解,递归策略只需少量的程序就可描述出解题过程所需要的多次重复计算,大大地减少了程序的代码量。递归的能力在于用有限的语句来定义对象的无限集合。一般来说,递归需要有边界条件、递归前进段和递归返回段。当边界条件不满足时,递归前进;当边界条件满足时,递归返回。

汉诺塔问题

https://www.cnblogs.com/antineutrino/p/3334540.html
- 目标:将n个盘子从A移动到C

  1. 开始情况
  2. A:n B:0 C:0
  3. F(n, A, B, C);
  4. 他的功能是将n个盘子以某个塔作为辅助,然后将A上的n个盘子移动到C。不用考虑是如何实现的,我们只知道它能完成移动就好!
  5. 这个方法的精妙之处在于,他考虑的是这样一种情况:
  6. --------------------------------
  7. 考虑已经成功将n-1个盘子移动到B上,此时只要将最大的盘子从A移动到C即可。
  8. fun(n-1,A,C,B)
  9. A:1 B:n-1 C:0
  10. 现在最大的盘子到了C上,因为它最大,所以其他盘子在它身上可以随意移动,此时可以无视它,那么场上的盘子数量只有B上的n-1个盘子。这个问题就变成了一个n-1的移动问题,只是开始的柱子变成了B而已。
  11. move(A->C)
  12. A:0 B:n-1 C:1
  13. 这时再把B上的n-1个盘子移动到C
  14. A:0 B:0 C:n
  15. --------------------------------
  16. // 这整个是一次递归过程。
  17. (中间参数是辅助柱子)
  18. func:
  19. if n!=0 then ;预定值
  20. func(n-1, a, c, b) ;将n-1个盘子由a移动到b,以c为辅助柱子
  21. move a[n] to c ;将a上的最后一个盘子移动到c
  22. func(n-1, b, a, c) ;将n-1个盘子由b移动到c,以a为辅助柱子
  23. endif ;完成

如果一个大问题可以分解成无数小一步的子问题,那么方法直接定义为大问题的解决方案!然后找到小问题和大问题之间的联系!

课本上的解决方法:(最后一个参数是辅助柱子)

  1. package demo2;
  2. import java.util.Scanner;
  3. public class TowerOfHanoi {
  4. public static void main(String[] args) {
  5. Scanner input = new Scanner(System.in);
  6. System.out.print("Enter number of disks: ");
  7. int n = input.nextInt();
  8. System.out.println("The moves are:");
  9. moveDisks(n, 'A', 'B', 'C');
  10. }
  11. public static void moveDisks(int n, char fromTower, char toTower, char auxTower) {
  12. if (n == 1) {
  13. System.out.println("Move disk" + n + " from " + fromTower + " to " + toTower);
  14. } else {
  15. moveDisks(n - 1, fromTower, auxTower, toTower);
  16. System.out.println("Move disk" + n + " from " + fromTower + " to " + toTower);
  17. moveDisks(n - 2, auxTower, toTower, fromTower);
  18. }
  19. }
  20. }

尾递归优化

递归要消耗大量资源,而尾递归就不会。

在Java中谈尾递归:尾递归和垃圾回收的比较。
https://www.cnblogs.com/bellkosmos/p/5280619.html

  1. // 这是尾递归
  2. def tailrecsum(x, running_total=0):
  3. if x == 0:
  4. return running_total
  5. else:
  6. return tailrecsum(x - 1, running_total + x)
  7. // 这不是尾递归
  8. def recsum(x):
  9. if x == 1:
  10. return x
  11. else:
  12. return x + recsum(x - 1)

Java进阶篇已经开始更新了
链接:https://www.zybuluo.com/CLSChen/note/1438410

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注