复习Java-从python过渡

This article is categorized as "Garbage" . It should NEVER be appeared in your search engine's results.


很久没写过java了,此前很长一段时间里都写python,本文主要收集一些看起来和python不一样的地方。 够用就行 

本篇笔记 不包含 面向对象的public/private/abstract/interface...等内容。


基本数据类型

🔗 [Java 基本数据类型 | 菜鸟教程] https://www.runoob.com/java/java-basic-datatypes.html

8种基本数据类型:preview

类型转换

String转int, char, float, double...使用各自类型的 Double.parseDouble("1.114514") ,  Integer.parseInt("114514") , ... 抛出异常 NumberFormatException ,但 并不会不强制要求 catch Exception .

int, char, float, double...转String,统一使用 String.valueOf(xxx) 所有类型都能填

public class demo {
    public static void main(String[] args) {
        // String转double
        String str1 = "1.114514";
        double d = Double.parseDouble(str1);
        System.out.println(d);
        // 1.114514

        // String转int
        String str2 = "123";
        int i = Integer.parseInt(str2);
        System.out.println(i);
        // 123

        // int转String
        String val3 = String.valueOf(i);
        System.out.println(val3);
        // 123

        // String.valueOf()还可以使用int, char, float, boolean, ....
    }
}

浮点数

double和float

有double和float的区分,且double的精度比float高

有关整数除法,小数除法,精度

有关整数除法,小数除法,精度

public class demo {
    public static void main(String[] args) {
        System.out.println(3 / 2);          // 1
        System.out.println(3 / 2.0);        // 1.5
        System.out.println((float) 3 / 2);    // 1.5
        System.out.println((double) 3 / 2);    // 1.5
    }
}
public class demo {
    public static void main(String[] args) {
        System.out.println(1 / 3);          // 0
        System.out.println(1 / 3.0);        // 0.3333333333333333
        System.out.println((float) 1 / 3);    // 0.33333334
        System.out.println((double) 1 / 3);    // 0.3333333333333333
    }
}

BigDecimal

首先是一段代码,试图在Java中用常规方法(Math, double)计算[mathjax](\sqrt{2})^2[/mathjax]是否等于[mathjax]2[/mathjax] .

public class lecture {
    public static void main(String[] args) {
        if(Math.pow(Math.sqrt(2),2)==2){
            System.out.println("Math.pow(Math.sqrt(2),2)的结果为2");
        }else{
            System.out.println("Math.pow(Math.sqrt(2),2)的结果不为2");
        }
    }
}

// Math.pow(Math.sqrt(2),2)的结果不为2

如果引入一个很接近0的浮点数[mathjax]\epsilon=10^{-14}[/mathjax]:

public class demo {
    public static void main(String[] args) {
        double eps = 1e-14;
        if (Math.pow(Math.sqrt(2), 2) - 2 < eps) {
            System.out.println("Math.pow(Math.sqrt(2),2)和2的接近程度小于eps: " + (Math.pow(Math.sqrt(2), 2) - 2) + " < " + eps);
        } else {
            System.out.println("Math.pow(Math.sqrt(2),2)和2的接近程度大于eps: " + (Math.pow(Math.sqrt(2), 2) - 2) + " > " + eps);
        }
    }
}

// Math.pow(Math.sqrt(2),2)和2的接近程度小于eps: 4.440892098500626E-16 < 1.0E-14

但是我们注意到,在Java里面:

Float.MIN_VALUE = 1.4E-45
Double.MIN_VALUE = 4.9E-324

要想让结果逼近这两个更接近0的[mathjax]\epsilon[/mathjax],就要使用 BigDecimal 了:

import java.math.BigDecimal;
import java.math.MathContext;

public class Main {
    public static void main(String[] args) {
        System.out.println(Float.MIN_VALUE);    // 1.4E-45
        System.out.println(Double.MIN_VALUE);   // 4.9E-324
        BigDecimal sqrt = new BigDecimal(2).sqrt(new MathContext(324));
        BigDecimal sqrt2 = sqrt.pow(2);
        BigDecimal minus = sqrt2.add(new BigDecimal(-2));
        BigDecimal minus_abs = minus.abs();
        System.out.println(minus_abs);  // 1.755365...E-324
        System.out.println(new BigDecimal(Float.MIN_VALUE));    // 1.401298...E-45
        System.out.println(new BigDecimal(Double.MIN_VALUE));   // 4.940656458...E-324
        System.out.println(minus_abs.compareTo(new BigDecimal(Float.MIN_VALUE)));   // -1, gap < 1.401298...E-45
        System.out.println(minus_abs.compareTo(new BigDecimal(Double.MIN_VALUE)));  // -1, gap < 4.940656458...E-324
    }
}
Float.MIN_VALUE: 
1.4E-45

Double.MIN_VALUE: 
4.9E-324

用BigDecimal表示的gap:
1.7553650307552786834104640147489451426656313623012118352431838480274762801296239457483173231413486781454011184934233429206549379602764003089659139201681160454698974951744912285242105866207397328418704462027542366370688231103014415864621544859026041684045617816891111892125607087809393386227459297916158037460536929334574704E-324

用BigDecimal表示的Float.MIN_VALUE:
1.40129846432481707092372958328991613128026194187651577175706828388979108268586060148663818836212158203125E-45

用BigDecimal表示的Double.MIN_VALUE:
4.940656458412465441765687928682213723650598026143247644255856825006755072702087518652998363616359923797965646954457177309266567103559397963987747960107818781263007131903114045278458171678489821036887186360569987307230500063874091535649843873124733972731696151400317153853980741262385655911710266585566867681870395603106249319452715914924553293054565444011274801297099995419319894090804165633245247571478690147267801593552386115501348035264934720193790268107107491703332226844753335720832431936092382893458368060106011506169809753078342277318329247904982524730776375927247874656084778203734469699533647017972677717585125660551199131504891101451037862738167250955837389733598993664809941164205702637090279242767544565229087538682506419718265533447265625E-324

说明gap < Float.MIN_VALUE:
-1

说明gap < Double.MIN_VALUE:
-1

随机数

🔗 [How do I generate random integers within a specific range in Java? - Stack Overflow] https://stackoverflow.com/questions/363681/how-do-i-generate-random-integers-within-a-specific-range-in-java

int randomInt = ThreadLocalRandom.current().nextInt(0, 11);
// 生成[0, 11)范围内的整数,也就是[0, 10]范围内的整数
double randomDouble = ThreadLocalRandom.current().nextDouble(0, 11);
// 生成[0, 11)范围内的double

下面的程序会生成100个[0, 10]之间的整数和1000个[0, 11)之间的double,然后打印生成的最大/最小数据。

import java.util.concurrent.ThreadLocalRandom;

public class demo {
    public static void main(String[] args) {
        int minInt = Integer.MAX_VALUE;
        int maxInt = Integer.MIN_VALUE;
        for (int i = 0; i < 100; i++) {
            int randomInt = ThreadLocalRandom.current().nextInt(0, 11);
            if (randomInt < minInt) minInt = randomInt;
            if (randomInt > maxInt) maxInt = randomInt;
        }
        System.out.printf("(Integer) min: %d, max: %d\n", minInt, maxInt);

        double minDouble = Double.MAX_VALUE;
        double maxDouble = Double.MIN_VALUE;
        for (int i = 0; i < 1000; i++) {
            double randomDouble = ThreadLocalRandom.current().nextDouble(0, 11);
            if (randomDouble < minDouble) minDouble = randomDouble;
            if (randomDouble > maxDouble) maxDouble = randomDouble;
        }
        System.out.printf("(Double) min: %f, max: %f\n", minDouble, maxDouble);

    }
}

结果:

(Integer) min: 0, max: 10
(Double) min: 0.033906, max: 10.997501

 ThreadLocalRandom.current().nextDouble(0, 11) 这玩意真的能生成出 0 吗?它生成0的概率是多少(这里假设是ThreadLocalRandom真随机,不然就没法估算了)?待续。

String的修改和复制

和Python一样,Java里面的String也是 immutable ,所以涉及到String的操作无需担心任何“浅拷贝”、“对象引用”。

并且要注意, new String() 的写法是完全多余的,任何时候都没有必要这么写。参考1参考2(stackoverflow).

public class demo {
    public static void main(String[] args) {
        String str1 = "abc";
        String str2 = str1;
        str1 = "bcd";
        System.out.println(str1);
        // bcd
        System.out.println(str2);
        // abc
    }
}

接下来是有关String内容的修改,直接写类似这样的操作: str[2]='K'  当然是不行的:

public class demo {
    public static void main(String[] args) {
        String str = "abcde";
        str[2] = 'K';

        /*
        demo.java:4: error: array required, but String found
        str[2] = 'K';
           ^
        1 error
         */

    }
}

应该这么做:

public class demo {
    public static void main(String[] args) {
        String str1 = "abcdefg";
        // 错误写法:str1[3]='Z'

        // 使用StringBuilder或者StringBuffer
        StringBuilder str_temp = new StringBuilder(str1);
        str_temp.setCharAt(3, 'Z');
        str1 = str_temp.toString();
        System.out.println(str1);
        // abcZefg

        // 使用substring硬拼接
        String str2 = "abcdefg";
        str1 = str2.substring(0, 3) + 'Z' + str2.substring(4);
        System.out.println(str1);
        // abcZefg
    }
}

另外,无论是在python还是javascript,类似 str[2]='K' 的操作都是无效的。python会报错:“TypeError: 'str' object does not support item assignment”,而javascript虽然不会报错,但不会有任何效果。

有关String的地址

有关String的地址

首先我们知道,Java里面是不能用 == 来比较字符串内容是否相同的,要用 .equal() .

但是下面的代码仍然会返回2个true:

import java.util.Scanner;

public class demo {
    public static void main(String[] args) {
        System.out.println("abc" == "abc");   //true
        System.out.println("abc".equals("abc"));    //true
    }
}

我们还可以写:

import java.util.Scanner;

public class demo {
    public static void main(String[] args) {
        String a = "abc";
        String b = "abc";
        System.out.println(a == b);     //true  
        System.out.println(a.equals(b));    //true
    }
}

但是从这里开始就不能用 == 判断了:

import java.util.Scanner;

public class demo {
    public static void main(String[] args) {
        String a = "abc";
        Scanner sc=new Scanner(System.in);
        System.out.println("Enter b: ");
        String b = sc.next(); // 手动输入abc
        System.out.println(a == b);     //false
        System.out.println(a.equals(b));    //true
    }
}

或者这样写也不能用 == 判断:

import java.util.Scanner;

public class demo {
    public static void main(String[] args) {
        String a = "abc";
        String b = new String("abc");
        System.out.println(a == b);     //false
        System.out.println(a.equals(b));    //true
    }
}
import java.util.Scanner;

public class demo {
    public static void main(String[] args) {
        String a = new String("abc");
        String b = new String("abc");
        System.out.println(a == b);     //false
        System.out.println(a.equals(b));    //true
    }
}

这是因为我们分别创建了String Literal和String Object:

https://www.geeksforgeeks.org/string-initialization-java-string-literal-vs-string-object/

进一步了解String.intern():

参考这里:🔗 [java的string存放位置的疑惑 - SegmentFault 思否] https://segmentfault.com/q/1010000009341279

import java.util.Scanner;

public class demo {
    public static void main(String[] args) {
        System.out.println("a" == "a"); // true
        System.out.println("a" == new String("a"));  // false
        String a = "abc";
        Scanner sc = new Scanner(System.in);
        System.out.println("Enter b value: ");
        String b = sc.next(); // 手动输入abc
        System.out.println(a == b); // false
        System.out.println(a.equals(b));  // true
    }
}
import java.util.Scanner;

public class demo {
    public static void main(String[] args) {
        // https://segmentfault.com/q/1010000009341279
        System.out.println("a" == "a");  // true
        System.out.println("a" == new String("a"));  // false  
        String a = "abc";
        String b = new String("abc");
        System.out.println(a == b);  // false
        System.out.println(a == b.intern());  // true
        System.out.println(a.equals(b));  // true
    }
}

next()和nextline()

输入数据,特别注意next()和nextline()的区别:

java核心技术第10版卷I P57
import java.util.Scanner;

public class demo {
    public static void main(String[] args) {
        System.out.println("在下面一行输入 a b c");
        Scanner sc = new Scanner(System.in);
        String next = sc.next();
        System.out.println(next);
    }
}

把next()改为nextline():

import java.util.Scanner;

public class demo {
    public static void main(String[] args) {
        System.out.println("在下面一行输入 a b c");
        Scanner sc = new Scanner(System.in);
        String next = sc.nextLine();
        System.out.println(next);
    }
}

static方法

static方法

假设我现在只想写一个简单纯粹的function  sqrt() ,返回开根号的结果,那么我可以写:

代码1:

public class demo {
    public double sqrt(double f) {  // 这里有区别!
        return Math.pow(f, 0.5);
    }
    public static void main(String[] args) {
        for (double f = 1.0; f <= 10.0; f++) {
            System.out.println(new demo().sqrt(f));
        }
    }
}

也可以写:

public class demo {
    public static double sqrt(double f) {   // 这里有区别!
        return Math.pow(f, 0.5);
    }
    public static void main(String[] args) {
        for (double f = 1.0; f <= 10.0; f++) {
            System.out.println(sqrt(f));
        }
    }
}

运行结果都是一样的。

但是,在本程序的场景下仍然建议使用 static double sqrt() ,这是因为:程序只会生成一个static Object,不会每次都生成一个新的Object占用空间。


arr[]和ArrayList

如果使用场景比较简单(不要求兼容性/拓展性/性能),绝大多数场景都可以使用 for循环+排序+额外存储变量 来解决。

从python过渡:使用Object

(这部分内容是后面补充的,但写在最前面)

python的list是可以添加各种类型的数据的,java也可以这样做:

public class demo {
    public static void main(String[] args) {
        Object[] arr = {1, 9999999999999L, 3, "Demo", 1.114514};
        for (Object obj : arr) {
            System.out.println(obj);
        }
    }
}
import java.util.ArrayList;

public class demo {
    public static void main(String[] args) {
        ArrayList<Object> arrayList = new ArrayList<>();
        arrayList.add(1);
        arrayList.add("Demo");
        arrayList.add(1.4415141);
        arrayList.add(9999999999999L);
        for (Object obj : arrayList) {
            System.out.println(obj);
        }
    }
}

创建和打印

array和arraylist的区别:array本质上是静态的,类似C语言的数组array;arraylist类似python的list,可以动态调整

// 数组array,静态
int[] arr = {5, 1, 2, 3, 6, 10, 0, 5};

// 打印数组的方法: Arrays.toString()
System.out.println(Arrays.toString(arr)); 
//  [5, 1, 2, 3, 6, 10, 0, 5]


// 打印二维数组/高维数组:Arrays.deepToString()
int[][] arr = {{1, 2, 3}, {4, 5, 6}};
System.out.println(Arrays.deepToString(arr));
// [[1, 2, 3], [4, 5, 6]]
// arrayList,可动态调整
ArrayList<Integer> arrayList = new ArrayList<>();


// 可以直接打印ArrayList
ArrayList<Integer> arrayList = new ArrayList<>(Arrays.asList(5, 1, 2, 3, 6, 10, 0, 5));
System.out.println(arrayList);  // [5, 1, 2, 3, 6, 10, 0, 5]

是否可以修改

import java.util.ArrayList;
import java.util.Arrays;

public class demo {
    public static void main(String[] args) {
        int[] arr = {5, 1, 2, 3, 6, 10, 0, 5};
        ArrayList<Integer> arrayList = new ArrayList<>(Arrays.asList(5, 1, 2, 3, 6, 10, 0, 5));

//        不能修改int[] arr
//        arr.add(10);

//        可以修改arrayList
        arrayList.add(100);
        System.out.println(arrayList);
    }
}

默认初始化数值

https://blog.csdn.net/guanmao4322/article/details/83780652
import java.util.Arrays;

public class demo {
    public static void main(String[] args) {
        int[] arr = new int[10];
        System.out.println(Arrays.toString(arr));
        // [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
    }
}

访问元素/成员

array直接使用数组下标访问(但不能像python那样使用arr[-1]),ArrayList使用 .get(index) 来访问。

import java.util.ArrayList;
import java.util.Arrays;

public class demo {
    public static void main(String[] args) {
        int[] arr = {5, 1, 2, 3, 6, 10, 0, 5};
        System.out.println(arr[arr.length - 1]);  // 5
        
        ArrayList<Integer> arrayList = new ArrayList<>(Arrays.asList(5, 1, 2, 3, 6, 10, 0, 5));
        System.out.println(arrayList.get(arrayList.size() - 1));  // 5
    }
}

添加和移除

仅对ArrayList有效。讨论这些用法:

  1. append value at the end
  2. remove item by index
  3. remove item by value(fist occurance)
  4. remove all items by value
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;

public class demo {
    public static void main(String[] args) {
        ArrayList<Integer> arrayList = new ArrayList<>(Arrays.asList(5, 1, 2, 3, 6, 5, 10, 0, 5));
        // 初始化:[5, 1, 2, 3, 6, 5, 10, 0, 5]

        // 如果不声明index,就是末尾添加100
        arrayList.add(100);
        System.out.println(arrayList);
        // [5, 1, 2, 3, 6, 5, 10, 0, 5, 100]

        // 声明index=1,就会把101添加到index=1的位置上去
        arrayList.add(1, 101);
        System.out.println(arrayList);
        // [5, 101, 1, 2, 3, 6, 5, 10, 0, 5, 100]

        // 去掉一个末尾的元素
        arrayList.remove(arrayList.size() - 1);
        System.out.println(arrayList);
        // [5, 101, 1, 2, 3, 6, 5, 10, 0, 5]

        // 去掉第一个出现的5
        arrayList.remove(Integer.valueOf(5));
        System.out.println(arrayList);
        // [101, 1, 2, 3, 6, 5, 10, 0, 5]

        // 去掉所有的5
        arrayList.removeAll(Collections.singleton(5));
        System.out.println(arrayList);
        // [101, 1, 2, 3, 6, 10, 0]
    }
}

相互转换

arr[]转arrayList: 比想像中要复杂,没有什么特别简单的转换方法,都要稍微绕一下,多套几层

arrayList转arr[]: 通过 .toArray() 的方法转换只能得到 Object[] ,所以简单场景下还是还是手写循环来的快。

arr[]转arrayList:

import java.util.ArrayList;
import java.util.Arrays;

public class demo {
    public static void main(String[] args) {
        int[] arr = {5, 1, 2, 3, 6, 10, 0, 5};
//        相对复杂一些
        ArrayList<Integer> arrayList = new ArrayList<>(Arrays.stream(arr).boxed().toList());
        System.out.println(arrayList);


//        没有一步到位的捷径方法,如果场景简单,推荐使用手写for循环
        ArrayList<Integer> arrayList2 = new ArrayList<>(arr.length);
        for (int val : arr) arrayList2.add(val);
        System.out.println(arrayList2);
    }
}

arrayList转arr[]:

import java.io.*;
import java.util.ArrayList;
import java.util.Arrays;

public class demo {
    public static void main(String[] args) throws IOException {
        ArrayList<Integer> arrayList = new ArrayList<>(Arrays.asList(5, 1, 2, 3, 6, 10, 0, 5));

        // 通过.toArray()的方法转换为Object[]
        Object[] arr = arrayList.toArray();
        for (Object obj : arr) {
            System.out.print(obj + " ");
        }
        // 5 1 2 3 6 10 0 5

        // 手写循环
        int[] arr2 = new int[arrayList.size()];
        for (int i = 0; i < arrayList.size(); i++) {
            arr2[i] = arrayList.get(i);
        }
        System.out.println();
        System.out.println(Arrays.toString(arr2));
        // [5, 1, 2, 3, 6, 10, 0, 5]
    }
}

clone()是浅拷贝

类比于python的copy()和deepcopy(),Java里面也有类似的效果,

 但这并不意味着java里面的clone()非常好用。  各种正规教科书/问答网站里都不推荐使用clone(),而是推荐自己手写。目前可以把clone()看作是python里的copy(),很多时候并不如deepcopy()那样好用,但好像java也不提供什么简单易用的deepcopy()...
import java.util.Arrays;

public class demo {
    public static void main(String[] args) {
        int[] arr = {5, 1, 2, 3, 6, 10, 0, 5};
        int[] arr2 = arr; // 传递对数组的引用
        arr[0] = 100;
        System.out.println(Arrays.toString(arr));   // [100, 1, 2, 3, 6, 10, 0, 5]
        // arr2和arr共同指向同一个数组对象,所以arr2和arr都会受影响
        System.out.println(Arrays.toString(arr2));    //  [100, 1, 2, 3, 6, 10, 0, 5]
    }
}

ArrayList也有相同结论:

import java.util.ArrayList;
import java.util.Arrays;

public class demo {
    public static void main(String[] args) {
        ArrayList<Integer> arrayList = new ArrayList<>(Arrays.asList(5, 1, 2, 3, 6, 10, 0, 5));
        ArrayList<Integer> arrayList1 = arrayList;
        arrayList.set(0, 100);
        System.out.println(arrayList);  // [100, 1, 2, 3, 6, 10, 0, 5]
        System.out.println(arrayList1);  // [100, 1, 2, 3, 6, 10, 0, 5]
    }
}

参数传递

参数传递,首先是2个基本类型的参数传递:

int

public class demo {
    public static void change(int a) {
        a += 10;
    }

    public static void main(String[] args) {
        int a = 10;
        change(a);
        System.out.println(a);
        // 不变;还是10
    }
}

String

public class demo {
    public static void change(String a) {
        a += "value";
    }

    public static void main(String[] args) {
        String a = "123";
        change(a);
        System.out.println(a);
        // 不变;还是"123"
    }
}

arr[]和ArrayList:

import java.util.Arrays;

public class demo {
    public static void changeValue(int[] arr) {
        arr[0] = 100;
    }

    public static void main(String[] args) {
        int[] arr = {5, 1, 2, 3, 6, 10, 0, 5};
        changeValue(arr);
        System.out.println(Arrays.toString(arr));  // [100, 1, 2, 3, 6, 10, 0, 5]
    }
}
import java.util.ArrayList;
import java.util.Arrays;

public class demo {
    public static void changeValue(ArrayList<Integer> arrayList) {
        arrayList.set(0, 100);
    }

    public static void main(String[] args) {
        ArrayList<Integer> arrayList = new ArrayList<>(Arrays.asList(5, 1, 2, 3, 6, 10, 0, 5));
        changeValue(arrayList);
        System.out.println(arrayList);  // [100, 1, 2, 3, 6, 10, 0, 5]
    }
}

排序

自带的排序算法sort()

Arrays.sort()和Collections.sort()分别提供了对 数组  ArrayList 的排序:

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;

public class demo {
    public static void main(String[] args) {
        int[] arr = {5, 1, 2, 3, 6, 10, 0, 5};
        Arrays.sort(arr);
        System.out.println(Arrays.toString(arr));  
        // [0, 1, 2, 3, 5, 5, 6, 10]

        ArrayList<Integer> arrayList = new ArrayList<>(Arrays.asList(5, 1, 2, 3, 6, 10, 0, 5));
        Collections.sort(arrayList);
        System.out.println(arrayList);
        // [0, 1, 2, 3, 5, 5, 6, 10]
    }
}

max()和min()

最大值max()和最小值min():

对ArrayList而言相对比较简单,只需要使用Collections.max()和Collextions.min()即可:

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;

public class demo {
    public static void main(String[] args) {
        ArrayList<Integer> arrayList = new ArrayList<>(Arrays.asList(5, 1, 2, 3, 6, 10, 0, 5));
        System.out.println(Collections.max(arrayList));  // 10
        System.out.println(Collections.min(arrayList));  // 0
    }
}

而数组就没有那么一步到位的方法了,目前看来只能用一些Java 8 以后的方法了:

import java.util.Arrays;

public class demo {
    public static void main(String[] args) {
        int[] arr = {5, 1, 2, 3, 6, 10, 0, 5};
        System.out.println(Arrays.stream(arr).max().getAsInt());  // 10
    }
}

搜索

搜索

没有什么特别好的方法,要注意 Arrays.binarySearch 非常难用,一般来说没有什么出场机会。

异常处理

由于Java在编译的时候会强制检查异常处理,所以这部分内容也算是必修项,绕不开的。

不同的try代码

下面的代码统一使用了 System.err.println() 来打印错误日志。

example 1(最常见):

public class demo {
    public static void main(String[] args) {
        try {
            int a = 10 / 0;
        } catch (Exception e) {
            System.err.println("catch Exception");
        }
    }
}

example 2:

类似 try(xxxxxx) { } catch { }  这样的语句

import java.io.*;
import java.util.Scanner;

public class demo {
    public static void main(String[] args) {
        try (Scanner sc = new Scanner(new File("nofile.txt"))) {
            // 这种写法要求括号内提供java.lang.AutoCloseable,并不是所有语句都可以用在这里的
            sc.close();
        } catch (FileNotFoundException e) {
            System.err.println("catch FileNotFoundException");
        }
    }
}

example 3:

对整个方法try catch调用

import java.io.*;
import java.util.Scanner;

public class demo {
    public static void writeSomething() throws FileNotFoundException {
        Scanner sc = new Scanner(new File("nofile.txt"));
        sc.close();
    }

    public static void main(String[] args) {
        try {
            writeSomething();
        } catch (FileNotFoundException e) {
            System.err.println("FileNotFoundException");
        }
    }
}

不同的catch代码

列举常见的几种catch代码,需要注意的是 throw new RuntimeException("xxxxxxx") 会中断当前程序。

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

        // style 1
        try {
            // something
        } catch (Exception e) {
            throw new RuntimeException("xxxxxxx");
            // 程序直接中断,后续代码不会运行
        }


        // style2
        try {
            // something
        } catch (Exception e) {
            e.printStackTrace();
            // 后续程序仍然会正常运行
        }

        // style3
        try {
            // something
        } catch (Exception e) {
            System.err.println("xxxxxxx");
            // 后续程序仍然会正常运行

        }

        // style4
        try {
            // something
        } catch (Exception e) {
            System.out.println("xxxxxxx");
            // 后续程序仍然会正常运行
        }


        System.out.println("程序还能继续运行");
    }
}

文件IO

简单的读写

暂时不考虑任何性能差异,这里使用了3个最容易学的File IO方法:

Scanner读,PrintWriter覆盖写,FileWriter追加写;

Scanner可以用nextLine()逐行读,也可以用nextInt(), nextFloat()等读法,和控制台输入的那个Scanner用法一致;

PrintWriter只能覆盖写;

FileWriter可以覆盖写,也可以追加写。

下面的代码让程序逐行读取一个文件 read.txt ,使用字符串保存每行的内容,然后原封不动写到一个新文件 write.txt 里面(此外还有追加写到 append.txt 里面):

import java.io.*;
import java.util.ArrayList;
import java.util.Scanner;

public class demo {

    public static void main(String[] args) throws IOException {
        ArrayList<String> readStrArr = new ArrayList<>();

        // 简单易懂的读取文件Scanner,几乎不需要掌握新的语法知识
        // 用法:Scanner.nextLine()
        Scanner f_sc = new Scanner(new File("./read.txt"));
        while (f_sc.hasNextLine()) {
            String result = f_sc.nextLine();
            // nextLine()逐行读取,这是最常见的读法
            // 还可以使用nextInt(), next()...等等
            readStrArr.add(result);
            System.out.println(result);
        }
        f_sc.close();

        // 简单易懂的写文件PrintWriter,就像System.out.println()一样的用法
        // PrintWriter是全覆盖的write
        // 用法: PrintWriter.println()和PrintWriter.print()
        PrintWriter pw = new PrintWriter(new File("./write.txt"));
        for (int i = 0; i < readStrArr.size(); i++) {
            if (i != readStrArr.size() - 1) pw.println(readStrArr.get(i));
                // 除了最后一行:使用println()添加一个换行
            else pw.print(readStrArr.get(i));
            // 最后一行不需要换行,所以使用print()
        }
        pw.close();

        // 还算简单易懂的写文件FileWriter,构造的时候添加参数append:true,变成追加模式a+
        // 用法:FileWriter.write(String)
        FileWriter fw = new FileWriter("./append.txt", true);
        fw.write('\n');
        for (int i = 0; i < readStrArr.size(); i++) {
            if (i != readStrArr.size() - 1) fw.write(readStrArr.get(i) + '\n');
            else fw.write(readStrArr.get(i));
            // 同理,除了最后一行不需要换行,其他的行都需要添加\n来换行
        }
        fw.close();
    }
}

异常处理

此外还要特别注意对异常处理的一些细节。注意下面的代码是有问题的:

import java.io.File;
import java.io.FileNotFoundException;
import java.util.Scanner;

public class demo {
    public static void main(String[] args) {
        // 假设确实不存在./input.txt文件
        Scanner sc = null;
        try {
            sc = new Scanner(new File("./input.txt"));
        } catch (FileNotFoundException e) {
            // 问题出在这里,使用了简单的打印语句
            System.err.println("File not found");
            // File not found
        }
        sc.close();
    }
}


//  File not found
//  Exception in thread "main" java.lang.NullPointerException: Cannot invoke "java.util.Scanner.close()" because "sc" is null
//  at demo.main(demo.java:15)

程序一开始设定了 Scanner sc = null ,但捕获错误时仅仅使用了System.err.println(),所以程序还会继续运行,运行到sc.close()的时候会抛出一个新的错误。

应该这么改:

import java.io.File;
import java.io.FileNotFoundException;
import java.util.Scanner;

public class demo {
    public static void main(String[] args) {
        // 假设确实不存在./input.txt文件
        Scanner sc = null;
        try {
            sc = new Scanner(new File("./input.txt"));
        } catch (FileNotFoundException e) {
            throw new RuntimeException("File not found");
        }
        sc.close();
    }
}

或者继续使用 System.err.println() ,但最后要检查Scanner是否为 null 

import java.io.File;
import java.io.FileNotFoundException;
import java.util.Scanner;

public class demo {
    public static void main(String[] args) {
        Scanner sc = null;
        try {
            sc = new Scanner(new File("./input.txt"));
        } catch (FileNotFoundException e) {
            System.err.println("File not found");
            // File not found
        }
        if (sc != null) sc.close();
    }
}

HashSet和HashMap

选这两个数据结构出来学习的原因是它们分别(近似的)对应python的set和dictionary

HashSet和HashMap是否会保留插入数据的顺序

两者都不会。

import java.util.HashSet;

public class demo {
    public static void main(String[] args) {
        HashSet<Object> hashSet = new HashSet<>();
        hashSet.add(3);
        hashSet.add(1);
        hashSet.add(2);
        for (Object obj : hashSet) {
            System.out.println(obj);
//            1
//            2
//            3
        }
    }
}
import java.util.HashMap;

public class demo {
    public static void main(String[] args) {
        HashMap<Integer, Integer> hashMap = new HashMap<>();
        hashMap.put(3, 100);
        hashMap.put(1, 100);
        hashMap.put(2, 100);
        for (int key : hashMap.keySet()) {
            System.out.println(key);
//            1
//            2
//            3
        }
    }
}

HashSet的基本用法

就像python的set那样

HashSet的常见方法有:

add:添加

remove:去除(可以去掉并不存在的元素而不引发报错)

contains:检查是否存在


HashSet里面的元素不允许重复,但:

你仍然可以试图向HashSet里面添加(add)重复元素(不会报错)

你仍然可以试图从HashSet里面移除(remove)一个不存在的元素(不会报错)

example:

初始化一个HashSet,并向里面丢入一堆东西:

import java.util.HashSet;

public class demo {
    public static void main(String[] args) {
        HashSet<Object> hashSet = new HashSet<>();
        hashSet.add(1);
        hashSet.add("New");
        hashSet.add("New"); // 不会报错
        hashSet.add(1);
        hashSet.add(1.0);
        hashSet.add(1.00);
        hashSet.add((2));

        hashSet.remove(100); // 不会报错

        for (Object obj : hashSet) {
            System.out.println(obj);
            //        1.0
            //        1
            //        New
            //        2
        }
    }
}

用HashSet去除重复元素

和python去掉list重复元素的思路类似:

python:先转为set,再转回list

java:先转换为HashSet,再转回去

import java.util.ArrayList;
import java.util.HashSet;

public class demo {
    public static void main(String[] args) {
        ArrayList<Object> arrayList = new ArrayList<>();
        arrayList.add(1);
        arrayList.add(2);
        arrayList.add("New");
        arrayList.add(3);
        arrayList.add(2);
        arrayList.add(4);
        arrayList.add("New");

        HashSet<Object> hashSet = new HashSet<>(arrayList);
        ArrayList<Object> arrayList_removeDup = new ArrayList<>(hashSet);
        for (Object obj : arrayList_removeDup) {
            System.out.println(obj);
        }
    }
}

HashMap的基本用法

就像python的dictionary那样

HashMap:

(和HashTable的区别这里暂时不讨论了)

常见方法有:

put:放入

get:拿取

containsKey:检查key是否存在

keySet:列出所有keys

values:列出所有values


使用 put() 的时候有可能做到2个动作:1,添加新的key-value(如果它没有存在于HashMap)2,基于已有的key,更新现有key-value的value

import java.util.HashMap;

public class demo {
    public static void main(String[] args) {
        HashMap<Object, Object> hashMap = new HashMap<>();
        hashMap.put("key", "value");
        hashMap.put(123, "234");
        hashMap.put("1234", 1234);
        hashMap.put(123, "2345");
        System.out.println(hashMap.get(123));
        // 2345
    }
}


 Last Modified in 2023-05-04 

Leave a Comment Anonymous comment is allowed / 允许匿名评论