Java基础学习笔记

4 篇文章 1 订阅

java基础入门

Jetbrains系统产品无限重置试用期教程(Windows/MacOS/Linux通用)

http://www.520xiazai.com/soft/jetbrains-eval-reset.html

JDK命令说明

java:这个可执行程序其实就是JVM,运行Java程序,就是启动JVM,然后让JVM执行指定的编译后的代码;
javac:这是Java的编译器,它用于把Java源码文件(以.java后缀结尾)编译为Java字节码文件(以.class后缀结尾);
jar:用于把一组.class文件打包成一个.jar文件,便于发布;
javadoc:用于从Java源码中自动提取注释并生成文档;
jdb:Java调试器,用于开发阶段的运行调试。

基础概念

类和方法

// class类名是Hello,class用来定义一个类,public表示这个类是公开的,花括号{}中间则是类的定义
public class Hello {
	// 方法名main,还有用()括起来的方法参数,这里的main方法有一个参数,参数类型是String[],参数名是args,public、static用来修饰方法,这里表示它是一个公开的静态方法,void是方法的返回类型,而花括号{}中间的就是方法的代码。
    public static void main(String[] args) {
        System.out.println("Hello, world!");
    }
}
编译
javac Hello.java
执行
java Hello
Hello, world!
  • 一个Java源码只能定义一个public类型的class,并且class名称和文件名要完全一致;

基本数据类型

基本数据类型是CPU可以直接进行运算的类型。Java定义了以下几种基本数据类型:

整数类型(byte,short,int,long)

各种整型能表示的最大范围如下:
byte:-128 ~ 127
short: -32768 ~ 32767
int: -2147483648 ~ 2147483647
long: -9223372036854775808 ~ 9223372036854775807

浮点数类型(float,double)

float f1 = 3.14f;
float f2 = 3.14e38f; // 科学计数法表示的3.14x10^38
double d = 1.79e308;
double d2 = -1.79e308;
double d3 = 4.9e-324; // 科学计数法表示的4.9x10^-324

字符类型(char)

字符类型char表示一个字符
char a = ‘A’;
注意char类型使用单引号’,且仅有一个字符,要和双引号"的字符串类型区分开。

布尔类型(boolean)

boolean b1 = true;
boolean b2 = false;
boolean isGreater = 5 > 3; // 计算结果为true
int age = 12;
boolean isAdult = age >= 18; // 计算结果为false

引用类型

引用类型的变量类似于C语言的指针,它内部存储一个“地址”,指向某个对象在内存的位置

常量(final)

定义变量的时候,如果加上final修饰符,这个变量就变成了常量,常量名通常全部大写。
final double PI = 3.14;
常量在定义时进行初始化后就不可再次赋值,再次赋值会导致编译错误。

运算符

整数运算

+-*/
自增/自减:i++ / i--

与/或/非: && || !

boolean result = true && (5 / -1 > 0);

三元运算符 b ? x : y // 变量b是true则结果为x,false则结果为y

int age = 7;
boolean isPrimaryStudent = false;
System.out.println(isPrimaryStudent ? "Yes" : "No");  //No

强制类型转换

int n1 = (int) 12.3; 浮点数转整型
String s = "" + 12121; 数字转字符串
int y = Integer.parseInt(s); 字符串转数字

字符串

转义字符串

\" 表示字符"
\' 表示字符'
\\ 表示字符\
\n 表示换行符
\r 表示回车符
\t 表示Tab
\u#### 表示一个Unicode编码的字符

字符串连接“+”

// 如果用+连接字符串和其他数据类型,会将其他数据类型先自动转型为字符串,再连接

多行字符串

String s = "first line \n"
         + "second line \n"
         + "end";

字符串不可变

		String s = "hello";
        String t = s; // t在内存中指向了hello这个字符串
        s = "world"; // s 指向了world这个字符串
        System.out.println(t); // t还是"hello"

空字符串是一个有效的字符串对象,它不等于空值null,null不指向任何对象。

字符串常用操作

		String s = "Hello ";
        String ss = s.trim(); // Hello 去掉首尾空格
        String sss = s.replace('H', '-'); // -ello 替换字符串
        String s2 = "Hello";
        System.out.println(s.equalsIgnoreCase(s2)); // 判断忽略大小写后相等
        s = s.toUpperCase(); // HELLO 转换为大写
        s = s.toLowerCase(Locale.ROOT); // hello 转换为小写
        System.out.println(s.isEmpty()); // 判断字符串是否为空
        System.out.println(s.length()); // 获取字符串长度

        String s3 = "A,B,C,D";
        String[] ss3 = s3.split("\\,"); // 字符串分割
        System.out.println(Arrays.toString(ss3)); // [A, B, C, D]
        String[] arr = {"A", "B", "C"};
        String s4 = String.join("***", arr); // 连接/拼接字符串 "A***B***C"
        String s5 = String.valueOf(true); // true 其它类型转字符串
        int n1 = Integer.parseInt("123"); // 123 字符串转int
        boolean b1 = Boolean.parseBoolean("true"); // true 字符串转bool
        char[] cs = "Hello".toCharArray(); // String -> char[]
        String s7 = new String(cs); // char[] -> String

数组

数组声明

		* 声明int数组并赋值
        int[] ns = new int[] { 68, 79, 91, 85, 62 }; 
        int[] ns0 = { 68, 79, 91, 85, 62 }; // 上面的简写
        System.out.println(ns.length); // 编译器自动推算数组大小为5
        int[] ns1 = new int[5]; // 声明一个大小为5的数组
        System.out.println(ns1[3]);

        * 声明String数组names并赋值
        // ***字符串是引用类型***
        String[] names = {"ABC", "XYZ", "zoo"}; // 声明String数组names并赋值
        String s = names[1]; // s指向了 "ABC"
        names[1] = "cat"; // names[1]指向了 "cat"
        System.out.println(s); // s还是"XYZ"
        
        * 声明二维数组
        int[][] ns = {
                { 1, 2, 3, 4 },
                { 5, 6, 7, 8 },
                { 9, 10, 11, 12 }
        };
        System.out.println(ns.length); // 3
        System.out.println(Arrays.deepToString(ns)); // 打印多维数组
        System.out.println(ns[0][1]); // 根据下标访问元素
}

数组遍历

public class Tes {
    public static void main(String[] args) {
        int[] ns = { 1, 4, 9, 16, 25 };
        for (int i=0; i<ns.length; i++) {
            int n = ns[i];
            System.out.printf("index=%d value=%d\n", i,n);
        }
    }

Arrays.toString()获取数组内容

直接打印数组变量,得到的是数组在JVM中的引用地址:[I@60e53b93
可以使用标准库的Arrays.toString()获取数组内容

		int[] ns = { 1, 4, 9, 16, 25 };
        System.out.println(ns); // [I@60e53b93
        System.out.println(Arrays.toString(ns)); //[1, 4, 9, 16, 25]

Arrays.deepToString()获取多维数组内容

Arrays.sort()数组排序

		int[] ns = { 3, 5, 10, 2, 11 };
        Arrays.sort(ns);
        System.out.println(Arrays.toString(ns)); //[2, 3, 5, 10, 11]

流程控制

浮点数计算判断

浮点数常常在计算机中精确表示,计算可能出现误差,判断浮点数用==号不准确,可以利用如下差值小于某个临界值计算。

public class Tes {
    public static void main(String[] args) {
        double x = 1 - 9.0 / 10;
        if (Math.abs(x - 0.1) < 0.00001) {
            System.out.println("x is 0.1");
        } else {
            System.out.println("x is NOT 0.1");
        }
    }
}

判断值类型的变量是否相等,可以使用==运算符。

判断引用类型变量是否相等,使用equals()。

public class Tes {
    public static void main(String[] args) {
        String s1 = "hello";
        String s2 = "HELLO".toLowerCase();
        System.out.println(s1);
        System.out.println(s2);
        // 通过 && 多个条件,避免 NullPointerException 错误
        if (s1 != null && s1.equals(s2)) {
            System.out.println("s1 equals s2");
        } else {
            System.out.println("s1 not equals s2");
        }
    }
}

格式化输出/用户输入

        double d = 3.1415926;
        System.out.printf("%f\n", d); // 全都打印
        System.out.printf("%.2f\n", d); // 只显示两位小数3.14
        System.out.printf("%.4f\n", d); // 只显示4位小数3.1416

        Scanner scanner = new Scanner(System.in); // 创建Scanner对象
        System.out.print("Input your name: "); // 打印提示
        String name = scanner.nextLine(); // 读取一行输入并获取字符串
        System.out.print("Input your age: "); // 打印提示
        int age = scanner.nextInt(); // 读取一行输入并获取整数
        System.out.printf("Hi, %s, you are %d\n", name, age); // 格式化输出

if else 判断

if (条件) {
    // 条件满足时执行
} else if (条件) {
    // 条件满足时执行
} else {
    // 上面条件都不满足时执行
}

switch case 多重选择

  • 注意语句后的break,不加break会导致将所有的条件都匹配一次,危险!
int option  = 1;
switch (option) {
case 3:
    ...
    break;
case 2:
    ...
    break;
case 1:
    ...
    break;
}

while循环(先判断循环条件,再执行循环)

while (条件表达式) {
    循环语句
}

public class Tes {
    public static void main(String[] args) {
        int sum = 0;
        int n = 1;
        while (n <= 100) {
            sum = sum + n;
            n ++;
        }
        System.out.println(sum); // 5050
    }
}

do while循环(先执行循环,再判断条件)

do {
    执行循环语句
} while (条件表达式);

public class Tes {
    public static void main(String[] args) {
        int sum = 0;
        int n = 1;
        do {
            sum = sum + n;
            n ++;
        } while (n <= 100);
        System.out.println(sum); // 5050
    }
}

for循环

for (初始条件; 循环检测条件; 循环后更新计数器) {
    // 执行语句
}

public class Tes {
    public static void main(String[] args) {
        int sum = 0;
        for (int i=1; i<=100; i++) {
            sum = sum + i;
        }
        System.out.println(sum);
    }
}

break和continue

  • break会跳出当前循环,也就是整个循环都不会执行了
  • continue则是提前结束本次循环,直接继续执行下次循环

获取命令行参数

public class Tes {
    public static void main(String[] args) {
        for (String arg : args) { // 获取命令行参数
            if ("-version".equals(arg)) { // 匹配命令行参数则打印
                System.out.println("v1.0");
                break;
            }
        }
    }
}

java基础入门2

面向对象编程

方法

方法定义

修饰符 方法返回类型 方法名(方法参数列表) {
    若干方法语句;
    return 方法返回值;
}
public String getName(){
    return name;
}

构造方法

class Person {
	构造方法:名称就是类名。参数没有限制,且没有返回值
    public Person() {
    }
}

方法例子

  • java中通常使用方法(method)操作private field,如get方法、set方法
  • this始终指向当前实例。因此,通过this.field就可以访问当前实例的字段。
public class Tes {
    public static void main(String[] args) {
        Person p = new Person("zzd", 10); // 实例化class
        p.setName("zzd"); // 赋值
        p.setBirth(2000); // 赋值
        System.out.println(p.getAge());
    }
}

class Person { // class
    private String name;
    private int age;
    private int birth;
    // 构造方法:名称就是类名。参数没有限制,且没有返回值
    // 默认构造方法,实例化时候:Person p = new Person("");
    public Person(){
    }
    // 自定义构造方法,实例化时候:Person p = new Person("zzd", 10);
    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }
    // java中通常使用方法(method)操作private field
    // set方法的主要作用就是可以针对传的参数做处理
    public void setName(String name) {
        if (name == null || name.isEmpty()) {
            throw new IllegalArgumentException("invaild name");
        }
        this.name = name;
    }
    // 调用calcAge method计算年龄
    public int getAge() {
        return calcAge(2021);
    }

    public void setBirth(int birth) {
        this.birth = birth;
    }
    // private 方法只能在内部调用
    private int calcAge(int currentYear) {
        return currentYear - this.birth;
    }
}

可变参数写法

class Group {
    private String[] names;

    public void setNames(String... names) {
        this.names = names;
    }
}

方法重载(名称相同,参数不同)

	public void hello(){
        System.out.println("hello");
    }
    public void hello(String name){
        System.out.printf("hello %s", name);
    }

继承和多态

  • 子类自动获得了父类的所有字段,严禁定义与父类重名的字段!所有类的根类是Object,关键字extends继承
  • 子类无法访问父类的private字段或者private方法,若想访问使用protected
  • 子类引用父类的字段时,可以用super.fieldName
  • final关键字阻止继承
  • 多态:子类可以覆写父类的方法(Override),覆写在子类中改变了父类方法的行为;
关键字 final 阻止类的继承
final class Me {
    public int id;
}
// Cannot inherit from final 'Me'
继承使用 extends
class Student extends Person {
    public int score;
}
多态:子类可以覆写父类的方法(Override),覆写在子类中改变了父类方法的行为
class Person {
    public void run() {
        System.out.println("person");
    }
}

class Student extends Person {
	// 同名方法Override
    @Override
    public void run() {
        System.out.println("student");
    }
}

抽象 abstract 和接口 interface

  • abstract 抽象类和 abstract 抽象方法提供了"规范",规定子类继承时必须重写该 abstract 抽象方法,关键字 abstract
  • 子类继承抽象类必须Override定义的 abstract 抽象方法
  • interface 比抽象类还要抽象的纯抽象接口,因为它连字段都不能有,定义的所有方法默认都是public abstract的。使用implements实现接口,接口也可以继承从而实现扩展。
  • interface default方法:当我们需要给接口新增一个方法时,会涉及到修改全部子类。如果新增的是default方法,那么子类就不必全部修改,只需要在需要覆写的地方去覆写新增方法。
public class Tes {
    public static void main(String[] args) {
        Person p = new Student();
        p.run(); // Student.run
        Person t = new Teacher();
        t.run(); // Teacher.run

        Cat c = new Cat("huahua");
        c.run(); // huahua run
        c.age(); // age
    }
}


// abstract 抽象类和 abstract 抽象方法提供了"规范",规定子类继承时必须重写该 abstract 抽象方法,关键字 abstract
// 使用 abstract 抽象方法必须也把类定义为 abstract 抽象类
// 抽象类
abstract class Person {
    // 抽象方法
    public abstract void run();
}
// 子类继承抽象类必须Override定义的 abstract 抽象方法
class Student extends Person {
    @Override
    public void run() {
        System.out.println("Student.run");
    }
}

class Teacher extends Person {
    @Override
    public void run() {
        System.out.println("Teacher.run");
    }
}


// interface 比抽象类还要抽象的纯抽象接口,因为它连字段都不能有,定义的所有方法默认都是public abstract的
interface Hi {
    void say();
}
// 接口也可以继承,扩展了接口方法
interface Animal extends Hi {
    void run();
    // 实现类可以不必覆写default方法。default方法的目的是,当我们需要给接口新增一个方法时,会涉及到修改全部子类。如果新增的是default方法,那么子类就不必全部修改,只需要在需要覆写的地方去覆写新增方法。
    default void age() {
        System.out.println("age");
    };
}


// 当一个具体的class去实现一个interface时,需要使用implements关键字
// 一个类只能继承自另一个类,不能从多个类继承。但是,一个类可以实现多个interface
class Cat implements Animal {
    private String name;

    public Cat(String name) {
        this.name = name;
    }
    public String getName(String name){
        return this.name;
    }

    @Override
    public void run() {
        System.out.println(this.name + " run");
    }

    @Override
    public void say(){
        System.out.println("hello" + this.name);
    }
}

静态字段和静态方法

  • 对于 static 字段,无论修改哪个实例的静态字段,效果都是一样的:所有实例的静态字段都被修改了,原因是静态字段并不属于实例,且全局共用。
  • 通过类名可以直接调用,可以访问静态字段和其他静态方法;静态方法常用于工具类
public class Tes {
    public static void main(String[] args) {
        Person ming = new Person("Xiao Ming", 12);
        Person hong = new Person("Xiao Hong", 15);
        ming.number = 88; // ming调用static field
        System.out.println(hong.number); // 88    hong的 number field同步,可以看出是static field是全局共用
        Person.setNumber(10); // 调用static方法
        System.out.println(Person.number); // 10
    }
}

class Person {
    public String name;
    public int age;
    // 对于 static 字段,无论修改哪个实例的静态字段,效果都是一样的:所有实例的静态字段都被修改了,原因是静态字段并不属于实例;全局共用
    public static int number;
    // 构造方法
    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }
    // 通过类名可以直接调用,可以访问静态字段和其他静态方法;
    public static void setNumber(int value) {
        number = value;
    }
}

常用类

Math 数学计算

        Math.abs(-10); // 10
        Math.max(1, 2); // 2
        Math.min(1.2, 2.3); // 1.2
        Math.pow(2, 10); // 2的10次方=1024
        double pi = Math.PI; // 3.141592..
        double x = Math.random(); // x的范围是[0,1)
        double min = 1;
        double max = 100;
        double y = x * (max - min) + min; // y的范围是[1,100)的小数
        long n = (long) y; // n的范围是[1,100)的整数
        System.out.println(y);
        System.out.println(n);

enum 枚举类

  • enum类型继承自java.lang.Enum,且无法被继承
  • 无需通过new创建实例即可调用
  • 定义的每个实例都是引用类型的唯一实例
  • 可以将enum类型用于switch语句
public class Tes {
    public static void main(String[] args) {
        // 无需通过new创建实例即可调用
        Gender g = Gender.BOY;
        switch (g) {
            case BOY:
                System.out.printf("%d: %s", g.index, g);
                break;
            case GIRL:
                System.out.printf("%d: %s", g.index, g);
                break;
        }
    }
}

// enum 枚举类:个人理解属于自定义数据类型,Gender.BOY 属于Gender。(与常量定义比较优雅)
enum Gender {
    // 定义 enum 中的常量
    BOY(1, "男"), GIRL(2, "女");

    public final int index;
    private final String sex;

    Gender(int index, String sex) {
        this.index = index;
        this.sex = sex;
    }

    // 默认情况下,对枚举常量调用toString()会返回和name()一样的字符串。但是,toString()可以被覆写,而name()则不行。我们可以给Gender添加toString()方法,使得输出结果更具可读性
    @Override
    public String toString() {
        return this.sex;
    }
}

异常处理

  1. throw new NullPointerException(); 主动抛出异常
  2. e.printStackTrace(); 打印异常堆栈
  3. try … finally 其中 finally 总是最后执行
  4. NullPointerException 是代码逻辑错误,用好的编程习惯尽量避免此类问题。如:初始化时传入默认值:private String name = “”; 在return时返回字符串""/空数组:return new String[0];
public class Tes {

    public static void main(String[] args) {
        // try ... catch捕获异常。catch捕获对应的Exception及其子类。
        try {
            process1();
//            int[] ns = new int[] { 68, 79, 91, 85, 62 };
//            System.out.println(ns[10]);
        } catch (NumberFormatException e) { // 异常捕获的问题很重要,子异常要定义在基异常前面
            e.printStackTrace(); // 打印异常堆栈
            System.out.println("NumberFormatException err");
        } catch (Exception e) { // Exception 为基异常
            e.printStackTrace();
            System.out.println("Exception error");
        } finally { // finally语句不是必须的,可写可不写。总是最后执行。
            System.out.println("finally");
        }
    }
    static void process1() {
        try {
            process2();   // process1 调用 process2 从而触发了throw new 异常
        } catch (NullPointerException e) {
            throw new IllegalArgumentException(e);
        }

    }
    static void process2() {
        throw new NullPointerException();  // throw 语句主动抛出异常
    }
}

/* output
java.lang.IllegalArgumentException: java.lang.NullPointerException  // 最外层的异常
	at Tes.process1(Tes.java:27)
	at Tes.main(Tes.java:10)
Caused by: java.lang.NullPointerException // 通过 Caused by 找到根异常
	at Tes.process2(Tes.java:33)
	at Tes.process1(Tes.java:25)
	... 1 more
Exception error
finally
* */

自定义异常

  • 自定义异常体系时,推荐从RuntimeException派生“根异常”,再派生出业务异常;
  • 自定义异常时,应该提供多种构造方法。
public class BaseException extends RuntimeException {
    public BaseException() {
        super();
    }

    public BaseException(String message, Throwable cause) {
        super(message, cause);
    }

    public BaseException(String message) {
        super(message);
    }

    public BaseException(Throwable cause) {
        super(cause);
    }
}

public class UserNotFoundException extends BaseException {
}

public class LoginFailedException extends BaseException {
}

日志处理

Logging

日志级别:
SEVERE
WARNING
INFO
CONFIG
FINE
FINER
FINEST

import java.util.logging.Level;
import java.util.logging.Logger;

public class Main {
    public static void main(String[] args) {
        Logger logger = Logger.getGlobal();
        logger.info("start process...");
        logger.warning("memory is running out...");
        logger.fine("ignored.");
        logger.severe("process will be terminated...");
    }
}

反射

Reflection,Java的反射是指程序在运行期可以拿到一个对象的所有信息。

  1. 通过Class实例获取class信息的方法称为反射(Reflection)。
public class Main {
    public static void main(String[] args) {
        // 通过Class实例获取class信息
        Class cls1 = String.class; // 法1  class java.lang.String

        String s = "Hello";
        Class cls2 = s.getClass(); // 法2  class java.lang.String

        boolean sameClass = cls1 == cls2; // true
    }
}

注解

注解是放在Java源码的类、方法、字段、参数前的一种特殊“注释”
注解可以被编译器打包进入class文件,是一种用作标注的“元数据”。

Java的注解可以分为三类:

第一类是由编译器使用的注解,例如:

@Override:让编译器检查该方法是否正确地实现了覆写;
@SuppressWarnings:告诉编译器忽略此处代码产生的警告。
这类注解不会被编译进入.class文件,它们在编译后就被编译器扔掉了。

第二类是由工具处理.class文件使用的注解,比如有些工具会在加载class的时候,对class做动态修改,实现一些特殊的功能。这类注解会被编译进入.class文件,但加载结束后并不会存在于内存中。这类注解只被一些底层库使用,一般我们不必自己处理。

第三类是在程序运行期能够读取的注解,它们在加载后一直存在于JVM中,这也是最常用的注解。例如,一个配置了@PostConstruct的方法会在调用构造方法后自动被调用(这是Java代码读取该注解实现的功能,JVM并不会识别该注解)。

泛型

泛型就是编写模板代码来适应任意类型;
泛型的好处是使用时不必对类型进行强制转换,它通过编译器对类型进行检查;

集合

Java的集合类定义在java.util包中,支持泛型,Java集合使用统一的Iterator遍历,主要有如下3种:

  • List:一种有序列表的集合,按索引排列
  • Set:一种保证没有重复元素的集合
  • Map:一种通过键值(key-value)查找的映射表集合

List

import java.util.List;
import java.util.ArrayList;
import java.util.Iterator;

public class Main {
    public static void main(String[] args) {
        List list = new ArrayList();
        list.add("hi"); // 增
        list.add("World");
        list.add("zzd");
        // 法1:普通for循环
        for (int i = 0; i < list.size(); i++) {
            String s = (String) list.get(i);
            System.out.println(s);
        }
        list.remove(0); // 删
        // 法2:Iterator遍历效率更高
        for (Iterator<String> it = list.iterator(); it.hasNext(); ) {
            String s = it.next();
            System.out.println(s);
        }
    }
}
/*
hi
World
zzd
World
zzd
* */

Map

HashMap

import java.util.HashMap;
import java.util.Map;

public class Main {
    public static void main(String[] args) {
        Map<String, Integer> map = new HashMap<>(); // 新建HashMap实例
        map.put("zzd", 20); // 增
        map.put("dada", 40); // 增
        System.out.println(map.get("dada")); // 查
        map.replace("zzd", 33); // 改
        map.remove("dada"); // 删

        // 法1:keySet获取key,再根据key获取value
        for (String key : map.keySet()) {
            Integer value = map.get(key);
            System.out.println(key + " = " + value);
        }
        // 法2:使用entrySet可以直接获取key和value
        for (Map.Entry<String, Integer> entry : map.entrySet()) {
            String key = entry.getKey();
            Integer value = entry.getValue();
            System.out.println(key + " = " + value);
        }

        // 用Map实现根据name查询某个Student
        Student s = new Student("Xiao Ming", 99);
        Map<String, Student> map1 = new HashMap<>();
        map1.put("Xiao Ming", s); // 将"Xiao Ming"和Student实例映射并关联
        map1.put("zhangsan", new Student("zhangsan", 100)); // 也可以在put里new
        Student target = map1.get("Xiao Ming"); // 通过key查找并返回映射的Student实例
        System.out.println(target == s); // true,同一个实例
        System.out.println(target.score); // 99
        Student another = map1.get("Bob"); // 通过另一个key查找
        System.out.println(another); // 未找到返回null
        for (String key : map1.keySet()) {
            Student value = map1.get(key);
            System.out.println(key + " = " + value.score);
        }
    }
}

class Student {
    public String name;
    public int score;

    public Student(String name, int score) {
        this.name = name;
        this.score = score;
    }
}

/*
40
zzd = 33
zzd = 33
true
99
null
zhangsan = 100
Xiao Ming = 99
* */

EnumMap

如果Map的key是enum类型,推荐使用EnumMap,根据enum类型的key直接定位到内部数组的索引,既保证速度,也不浪费空间。

import java.util.Map;
import java.time.DayOfWeek;
import java.util.*;

public class Main {
    public static void main(String[] args) {
        Map<DayOfWeek, String> map = new EnumMap<>(DayOfWeek.class);
        map.put(DayOfWeek.MONDAY, "星期一");
        map.put(DayOfWeek.TUESDAY, "星期二");
        map.put(DayOfWeek.WEDNESDAY, "星期三");
        map.put(DayOfWeek.THURSDAY, "星期四");
        map.put(DayOfWeek.FRIDAY, "星期五");
        map.put(DayOfWeek.SATURDAY, "星期六");
        map.put(DayOfWeek.SUNDAY, "星期日");
        System.out.println(map);
        System.out.println(map.get(DayOfWeek.MONDAY));
    }
}

SortedMap之TreeMap

SortedMap保证遍历时以Key的顺序来进行排序。

import java.util.Map;
import java.util.*;

public class Main {
    public static void main(String[] args) {
        Map<String, Integer> map = new TreeMap<>();
        map.put("orange", 1);
        map.put("apple", 2);
        map.put("pear", 3);
        map.put("cc", 4);
        for (String key : map.keySet()) {
            System.out.println(key);
        }
        // apple, cc, orange, pear
    }
}

Set集合

Set用于存储不重复的元素集合

import java.util.*;

public class Main {
    public static void main(String[] args) {
        Set<String> set = new HashSet<>(); // 无序集合
//        Set<String> set = new TreeSet<>(); // 有序集合,TreeSet是有序的,因为它实现了SortedSet接口。
        System.out.println(set.add("xyz")); // true
        System.out.println(set.add("abc")); // true
        System.out.println(set.add("xyz")); // false,添加失败,因为元素已存在
        System.out.println(set.contains("xyz")); // true,元素存在
        System.out.println(set.contains("XYZ")); // false,元素不存在
        System.out.println(set.remove("hello")); // false,删除失败,因为元素不存在
        System.out.println(set.size()); // 2,一共两个元素
        System.out.println(set); // [abc, xyz]
    }
}

Queue 先进先出

PriorityQueue 优先队列

Deque 双端队列

Stack 后进先出

迭代器Iterator

Iterator是一种抽象的数据访问模型。使用Iterator相比for循环效率更高。

		List<String> list = new ArrayList<>();
        list.add("apple");
        list.add("pear");
        list.add("cc");
        list.add("orange");
		for (String s : list) {
            System.out.println(s);
        }

Collections操作集合

Collections类提供了一组工具方法来方便使用集合类:
排序/洗牌等操作。

import java.util.*;

public class Main {
    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        list.add("apple");
        list.add("pear");
        list.add("cc");
        list.add("orange");
        // 排序前
        System.out.println(list); // [apple, pear, cc, orange]
        Collections.sort(list);
        // 排序后
        System.out.println(list); // [apple, cc, orange, pear]

        List<Integer> list1 = new ArrayList<>();
        for (int i=0; i<10; i++) {
            list1.add(i);
        }
        // 洗牌前:
        System.out.println(list1);
        Collections.shuffle(list1);
        // 洗牌后:
        System.out.println(list1);
    }
}

日期与时间

java.time包提供了新的日期和时间API

import java.time.*;
import java.time.format.*;

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

        LocalDate d = LocalDate.now(); // 当前日期
        LocalTime t = LocalTime.now(); // 当前时间
        LocalDateTime dt = LocalDateTime.now(); // 当前日期和时间
        System.out.println(d); // 严格按照ISO 8601格式打印 2021-04-02
        System.out.println(t); // 严格按照ISO 8601格式打印 10:43:22.827
        System.out.println(dt); // 严格按照ISO 8601格式打印 2021-04-02T10:43:22.827
        // 自定义打印时间
        DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm:ss");
        System.out.println(dtf.format(LocalDateTime.now())); // 2021/04/02 10:43:22

    }
}

IO

文件/目录操作

File对象可以对文件/目录进行操作

import java.io.*;

public class Main {
    public static void main(String[] args) throws IOException {
        /**
        * 获取路径
        * **/
        File f = new File(".."); // 构造方法传入的路径(.表示当前目录,..表示上级目录)
        System.out.println(f.getPath()); // ..
        System.out.println(f.getAbsolutePath()); // 绝对路径,项目根目录: /Users/zhangzhidao/Desktop/project/java_test/..
        System.out.println(f.getCanonicalPath()); // 规范路径: 取决于new File("") 中的路径  这里是上级目录: /Users/zhangzhidao/Desktop/project

        /**
         * 判断是文件/目录
         * **/
        File f1 = new File("/Users/zhangzhidao/Desktop/project/java_test");
        File f2 = new File("/Users/zhangzhidao/Desktop/project/java_test/pom.xml");
        System.out.println(f1.isDirectory()); // true
        System.out.println(f2.isFile()); // true

        /**
         * 普通文件创建/删除,临时文件创建/删除
         * **/
        File file = new File("a.txt");
        file.createNewFile(); // 创建文件
        file.delete(); //删除文件
        File file1 = new File("tmp/tt");
        file1.mkdirs(); // 创建目录
        
        File ftmp = File.createTempFile("tmp-", ".txt"); // 提供临时文件的前缀和后缀
        f.deleteOnExit(); // JVM退出时自动删除
        System.out.println(ftmp.isFile()); // true
        System.out.println(ftmp.getAbsolutePath()); // /var/folders/v4/vrfcdf_s0h58vzl4vvzjp5q00000gn/T/tmp-4742037933201830112.txt

        /**
         * 列出所有文件和子目录
         * **/
        File[] fs1 = f.listFiles(); // 列出所有文件和子目录
        for (File ff : fs1) {
            System.out.println(ff);
        }
    }
}

文件读写

import java.io.*;

public class Main {
    /**
    * 以行为单位读取文件
    * */
    public static void readFileByLines(String fileName) {
        File file = new File(fileName);
        BufferedReader reader = null;
        try {
            System.out.println("以行为单位读取文件内容,一次读一整行:");
            reader = new BufferedReader(new FileReader(file));
            String tempString = null;
            int line = 1;
            // 一次读入一行,直到读入null为文件结束
            while ((tempString = reader.readLine()) != null) {
                // 显示行号
                System.out.println("line " + line + ": " + tempString);
                line++;
            }
            reader.close();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (reader != null) {
                try {
                    reader.close();
                } catch (IOException e1) {
                    e1.printStackTrace();
                }
            }
        }
    }
    /**
    * 追加内容至文件
    * */
    public static void appendMethod(String fileName, String content) {
        try {
            //打开一个写文件器,构造函数中的第二个参数true表示以追加形式写文件
            FileWriter writer = new FileWriter(fileName, true);
            writer.write(content);
            writer.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    public static void main(String[] args) throws IOException {
        // 读取的文件
        String fileName = "readme.txt";
        // 待写入的内容
        String content = "new append!";
        Main.readFileByLines(fileName);
        //追加
        Main.appendMethod(fileName, content);
        Main.appendMethod(fileName, "append end. \n");
        //显示文件内容
        Main.readFileByLines(fileName);

    }
}

Maven

Maven是一个Java项目管理和构建工具,它可以定义项目结构、项目依赖,并使用统一的方式进行自动化构建.

  • 使用pom.xml定义项目内容
  • 在Maven中声明一个依赖项可以自动下载并导入classpath;
  • 以-SNAPSHOT结尾的版本号会被Maven视为开发版本,开发版本每次都会重复下载

maven阿里镜像仓库配置

vim ~/.m2/settings.xml

<settings>
    <mirrors>
        <mirror>
            <id>aliyun</id>
            <name>aliyun</name>
            <mirrorOf>central</mirrorOf>
            <!-- 国内推荐阿里云的Maven镜像 -->
            <url>https://maven.aliyun.com/repository/central</url>
        </mirror>
    </mirrors>
</settings>

maven依赖引入

groupId:属于组织的名称,类似Java的包名;
artifactId:该jar包自身的名称,类似Java的类名;
version:该jar包的版本。

pom.xml 文件中commons-logging依赖示例:

    <dependencies>
        <dependency>
            <groupId>commons-logging</groupId>
            <artifactId>commons-logging</artifactId>
            <version>1.2</version>
        </dependency>
    </dependencies>

IDEA引入依赖生效
在这里插入图片描述

maven生命周期(lifecycle)和阶段(phase)

内置的default生命周期包含以下阶段(phase)

validate
initialize
generate-sources
process-sources
generate-resources
process-resources
compile
process-classes
generate-test-sources
process-test-sources
generate-test-resources
process-test-resources
test-compile
process-test-classes
test
prepare-package
package
pre-integration-test
integration-test
post-integration-test
verify
install
deploy

运行mvn package,Maven就会执行default生命周期,它会从开始顺序执行一直运行到package这个phase。

clean生命周期,它会执行以下3个阶段(phase)

pre-clean
clean (注意这个clean不是lifecycle而是phase)
post-clean

常用maven组合命令

mvn clean:清理所有生成的class和jar;
mvn clean compile:先清理,再执行到编译compile;
mvn clean test:先清理,再执行到test,因为执行test前必须执行compile,所以这里不必指定compile;
mvn clean package:先清理,再执行到打包package。

使用自定义插件

常用插件
maven-shade-plugin:打包所有依赖包并生成可执行jar;
cobertura-maven-plugin:生成单元测试覆盖率报告;
findbugs-maven-plugin:对Java源码进行静态分析以找出潜在问题。

pom.xml 中引入maven-shade-plugin插件如下:

	<build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-shade-plugin</artifactId>
                <version>3.2.1</version>
                <executions>
                    <execution>
                        <phase>package</phase>
                        <goals>
                            <goal>shade</goal>
                        </goals>
                        <configuration>
                            <transformers>
                                <transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
                                    <!--指定Java程序的入口-->
                                    <mainClass>Main</mainClass>
                                </transformer>
                            </transformers>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>

Maven模块管理

一个大项目可以拆成几个模块:

  • 通过继承在parent的pom.xml统一定义重复配置
  • 通过编译多个模块

JDBC(mysql)

mysql数据

-- 创建数据库learjdbc:
DROP DATABASE IF EXISTS learnjdbc;
CREATE DATABASE learnjdbc;

-- 创建登录用户learn/口令learnpassword
CREATE USER IF NOT EXISTS learn@'%' IDENTIFIED BY 'learnpassword';
GRANT ALL PRIVILEGES ON learnjdbc.* TO learn@'%' WITH GRANT OPTION;
FLUSH PRIVILEGES;

-- 创建表students:
USE learnjdbc;
CREATE TABLE students (
  id BIGINT AUTO_INCREMENT NOT NULL,
  name VARCHAR(50) NOT NULL,
  gender TINYINT(1) NOT NULL,
  grade INT NOT NULL,
  score INT NOT NULL,
  PRIMARY KEY(id)
) Engine=INNODB DEFAULT CHARSET=UTF8;

-- 插入初始数据:
INSERT INTO students (name, gender, grade, score) VALUES ('小明', 1, 1, 88);
INSERT INTO students (name, gender, grade, score) VALUES ('小红', 1, 1, 95);
INSERT INTO students (name, gender, grade, score) VALUES ('小军', 0, 1, 93);
INSERT INTO students (name, gender, grade, score) VALUES ('小白', 0, 1, 100);
INSERT INTO students (name, gender, grade, score) VALUES ('小牛', 1, 2, 96);
INSERT INTO students (name, gender, grade, score) VALUES ('小兵', 1, 2, 99);
INSERT INTO students (name, gender, grade, score) VALUES ('小强', 0, 2, 86);
INSERT INTO students (name, gender, grade, score) VALUES ('小乔', 0, 2, 79);
INSERT INTO students (name, gender, grade, score) VALUES ('小青', 1, 3, 85);
INSERT INTO students (name, gender, grade, score) VALUES ('小王', 1, 3, 90);
INSERT INTO students (name, gender, grade, score) VALUES ('小林', 0, 3, 91);
INSERT INTO students (name, gender, grade, score) VALUES ('小贝', 0, 3, 77);

安装驱动

		<dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.47</version>
            <scope>runtime</scope>
        </dependency>

使用PreparedStatement 增删改查,避免sql注入

import java.sql.*;

public class Main {

    public static void main(String[] args) throws SQLException {
        // JDBC连接的URL, 不同数据库有不同的格式:
        String JDBC_URL = "jdbc:mysql://localhost:3306/learnjdbc";
        String JDBC_USER = "learn";
        String JDBC_PASSWORD = "learnpassword";
        // Connection建立JDBC连接
        try (Connection conn = DriverManager.getConnection(JDBC_URL, JDBC_USER, JDBC_PASSWORD)) {
            /**
             * PreparedStatement 进行查询,可以避免sql注入
             * 查询
             * */
            try (PreparedStatement ps = conn.prepareStatement("SELECT id, grade, name, gender FROM students WHERE gender=? AND grade=?")) {
                // ps.setObject(参数索引上述sql中?的位置, 参数的值)
                ps.setObject(1, 1); // 参数索引?的位置是1,gender=1
                ps.setObject(2, 3); // 参数索引?的位置是2,grade=3
                // 查询结果 ResultSet
                try (ResultSet rs = ps.executeQuery()) {
                    while (rs.next()) {
                        long id = rs.getLong("id");
                        long grade = rs.getLong("grade");
                        String name = rs.getString("name");
                        String gender = rs.getString("gender");
                        System.out.printf("id: "+ id + "grade: "+ grade + "name: " +  name + "gender" +  gender + "\n");
                    }
                }
            }
            /**
             * 插入
             * */
            try (PreparedStatement ps = conn.prepareStatement(
                    "INSERT INTO students (id, grade, name, gender, score) VALUES (?,?,?,?,?)")) {
                ps.setObject(1, 999); // 注意:索引从1开始
                ps.setObject(2, 1); // grade
                ps.setObject(3, "Bob"); // name
                ps.setObject(4, 0); // gender
                ps.setObject(5, 100);
                int n = ps.executeUpdate(); // 1
            }
            /**
             * 更新
             * */
            try (PreparedStatement ps = conn.prepareStatement("UPDATE students SET name=? WHERE id=?")) {
                ps.setObject(1, "zzd"); // 注意:索引从1开始
                ps.setObject(2, 999);
                int n = ps.executeUpdate(); // 返回更新的行数
            }
            /**
             * 删除
             * */
            try (PreparedStatement ps = conn.prepareStatement("DELETE FROM students WHERE id=?")) {
                ps.setObject(1, 999); // 注意:索引从1开始
                int n = ps.executeUpdate(); // 删除的行数
            }
        }
    }
}

JDBC事务

事务中的所有SQL要么全部执行成功,要么全部不执行,即数据库事务具有ACID特性:

  • Atomicity:原子性
  • Consistency:一致性
  • Isolation:隔离性
  • Durability:持久性
        try (Connection conn = DriverManager.getConnection(JDBC_URL, JDBC_USER, JDBC_PASSWORD)) {
            try {
                // 关闭自动提交:
                conn.setAutoCommit(false);
                // 执行多条SQL语句:
                insert(); update(); delete();
                // 提交事务:
                conn.commit();
            } catch (SQLException e) {
                // 回滚事务:
                conn.rollback();
            } finally {
                conn.setAutoCommit(true);
                conn.close();
            }
        }

Batch批量高效执行SQL

对内容相同,参数不同的SQL,要优先考虑batch操作,性能很高。

			/**
             * Batch批量执行
             * */
            try (PreparedStatement ps = conn.prepareStatement("INSERT INTO students (name, gender, grade, score) VALUES (?, ?, ?, ?)")) {
                // 对同一个PreparedStatement反复设置参数并调用addBatch():

                for (int i = 0; i < 200; i++) {
                    ps.setString(1, "zz" + i);
                    ps.setBoolean(2, false);
                    ps.setInt(3, 5);
                    ps.setInt(4, 90);
                    ps.addBatch(); // 添加到batch
                }
                // 执行batch:
                int[] ns = ps.executeBatch();
                for (int n : ns) {
                    System.out.println(n + " inserted."); // batch中每个SQL执行的结果数量
                }
            }

JDBC连接池

数据库连接池是一种复用Connection的组件,它可以避免反复创建新连接,提高JDBC代码的运行效率;
JDBC连接池有一个标准的接口javax.sql.DataSource,注意这个类位于Java标准库中,但仅仅是接口。要使用JDBC连接池,我们必须选择一个JDBC连接池的实现。这里使用HikariCP实现

        <dependency>
            <groupId>com.zaxxer</groupId>
            <artifactId>HikariCP</artifactId>
            <version>2.7.1</version>
        </dependency>
import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;

import java.sql.*;
import javax.sql.DataSource;
public class Main {

    public static void main(String[] args) throws SQLException {
        // 数据库连接池是一种复用Connection的组件,它可以避免反复创建新连接,提高JDBC代码的运行效率;
        // 使用 HikariCP 配置 JDBC 连接池
        HikariConfig config = new HikariConfig();
        config.setJdbcUrl("jdbc:mysql://localhost:3306/learnjdbc");
        config.setUsername("learn");
        config.setPassword("learnpassword");
        config.addDataSourceProperty("connectionTimeout", "1000"); // 连接超时:1秒
        config.addDataSourceProperty("idleTimeout", "60000"); // 空闲超时:60秒
        config.addDataSourceProperty("maximumPoolSize", "10"); // 最大连接数:10
        DataSource ds = new HikariDataSource(config);
        // 使用连接池就使用ds.getConnection()替换DriverManager.getConnection()
        // 创建DataSource是一个非常昂贵的操作,所以通常DataSource实例总是作为一个全局变量存储
        try (Connection conn = ds.getConnection()) { // 在此获取连接
            /**
             * Batch批量执行
             * */
            try (PreparedStatement ps = conn.prepareStatement("INSERT INTO students (name, gender, grade, score) VALUES (?, ?, ?, ?)")) {
                // 对同一个PreparedStatement反复设置参数并调用addBatch():

                for (int i = 0; i < 200; i++) {
                    ps.setString(1, "zz" + i);
                    ps.setBoolean(2, false);
                    ps.setInt(3, 5);
                    ps.setInt(4, 90);
                    ps.addBatch(); // 添加到batch
                }
                // 执行batch:
                int[] ns = ps.executeBatch();
                for (int n : ns) {
                    System.out.println(n + " inserted."); // batch中每个SQL执行的结果数量
                }
            }
        } // 在此“关闭”连接
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值