Skip to content

基础入门 #1

@wuyuedefeng

Description

@wuyuedefeng

名词解释

  • JDK:Java Development Kit
  • JRE:Java Runtime Environment
  • JSR规范:Java Specification Request
  • JCP组织:Java Community Process

JSR是一系列的规范,从JVM的内存模型到Web程序接口,全部都标准化了。而负责审核JSR的组织就是JCP

  • RI:Reference Implementation
  • TCK:Technology Compatibility Kit

通常来说,RI只是一个“能跑”的正确的代码,它不追求速度; 一般大家都会选择一个有竞争力的商用或开源产品(TCK)

安装JDK

Oracle的官网下载最新的稳定版JDK

检查是否安装

java -version
# java version "16.0.2" 2021-07-20
# ...

SDK命令

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

基本语法

Java规定,某个类定义的public static void main(String[] args)是Java程序的固定入口方法,因此,Java程序总是从main方法开始执行。
eg: Hello.java, java的文件名要保持和类名相同。

public class Hello {
    public static void main(String[] args) {
        // System.out.println("Hello, world!");
        for (String arg : args) {
            if ("-version".equals(arg)) {
                System.out.println("v 1.0");
                break;
            }
        }
    }
}

运行: java Hello.java

基本数据类型、字符串、数组

基本数据类型

  • 整数类型:byte(1byte),short(2byte),int(4byte),long(8byte)
  • 浮点数类型:float(4byte),double(8byte)
  • 字符类型:char(2byte)
  • 布尔类型:boolean

demo

public class Hello {
    public static void main(String[] args) {
        // System.out.println("hello world");

        // byte:-128 ~ 127
        // short: -32768 ~ 32767
        // int: -2147483648 ~ 2147483647
        // long: -9223372036854775808 ~ 9223372036854775807
        int i1 = 1;
        int i2 = 2_000_000_000; // 加下划线更容易识别
        int i3 = 0xff0000; // 十六进制表示的16711680
        int i4 = 0b1000000000; // 二进制表示的512
        int i5 = (100 + 200) * (99 - 88); // 3300
        int i6 = 12345 / 67; // 184 两个整数相除只能得到结果的整数部分
        int i7 = 12345 % 67; // 12345÷67的余数是17
        // +=,-=,*=,/=,++(自加),--(自减),<<(左移),>>(右移,负数高位1不变),>>>(不管符号位, 右移后高位总是补0),&(按位与),|(按位或),~(非运算,01互换),^(异或),  ...
        int i8 = 2147483640 + 15; // -2147483641 数据溢出,不会报错, 解决方法:用long类型
        int i9 = (int) 12.3; // 12 强制类型转换,浮点数的小数部分会被丢掉, 四舍五入可以使用: int i9 = (int) (d + 0.5)
        // 要显示一个字符的Unicode编码,只需将char类型直接赋值给int类型即可:
        int i10 = 'A'; // 字母'A'的Unicodde编码是65
        int i11 = '中'; // 汉字'中'的Unicode编码是20013
        // long型的结尾需要加L
        long l1 = 9000000000000000000L;

        // 对于float类型,需要加上f后缀
        float f1 = 3.14f;
        float f2 = 3.14e38f; // 科学计数法表示的3.14x10^38
        double d1 = -1.79e308;
        double d2 = 4.9e-324; // 科学计数法表示的4.9x10^-324
        double d3 = 1.0 / 10;
        double d4 = 1 - 9.0 / 10;
        // 由于浮点数存在运算误差,所以比较两个浮点数是否相等常常会出现错误的结果。eg: d3 == d4, 解决办法: Math.abs(x - y) < 0.00001,可以认为相等
        int n = 5;
        // 类型提升
        double d5 = 1.2 + 24.0 / n; // 6.0, 参与运算的两个数其中一个是整型,那么整型可以自动提升到浮点型
        // 溢出 整数运算在除数为0时会报错,而浮点数运算在除数为0时,不会报错,但会返回几个特殊值
        //      NaN表示Not a Number
        //      Infinity表示无穷大
        //      -Infinity表示负无穷大
        double d6 = 0.0 / 0; // NaN
        double d7 = 1.0 / 0; // Infinity
        double d8 = -1.0 / 0; // -Infinity
        // 强制转型


        // char使用单引号(')
        char a = 'A';
        char zh = '中';
        // \\u+Unicode编码来表示一个字符, 注意是十六进制:
        char c3 = '\u0041'; // 'A',因为十六进制0041 = 十进制65
        char c4 = '\u4e2d'; // '中',因为十六进制4e2d = 十进制20013

        // boolean值 !, >, >=, <, <=, ==, !=, &&, ||
        boolean isZero = false; // false
        boolean isNonZero = !isZero; // true
        boolean isGreater = 5 > 3 && 2 > 1; // true
        boolean isTrue = 3 > 2 ? true : false; // 三元运算符

        // 字符串类型(引用类型)
        String s1 = ""; // 空字符串,包含0个字符
        // \是转义字符: \" 表示字符", \' 表示字符', \\ 表示字符\, \n 表示换行符, \r 表示回车符, \t 表示Tab, \\u#### 表示一个Unicode编码的字符
        String s2 = "abc\"xyz"; // 包含7个字符: a, b, c, ", x, y, z
        String s3 = "ABC\n\u4e2d\u6587"; // 包含6个字符: A, B, C, 换行符, 中, 文
        // 字符串连接
        String s4 = "hello" + " " + "world" + "!";
        // 多行字符串
        String s5 = "first line \n"
                + "second line \n"
                + "end";
        String s6 = """
                SELECT * FROM
                  users
                WHERE id > 100
                ORDER BY name DESC"""; // 共同的空格会被去掉; """ 写在下一行将代表最后加了一个 \n
        // 空值null
        String s7 = null; // s1是null
        String s8; // 没有赋初值值,s2也是null
        String s9 = s1; // s3也是null
        String s10 = ""; // s4指向空字符串,不是null

        // 数组 数组所有元素初始化为默认值,整型都是0,浮点型是0.0,布尔型是false;数组一旦创建后,大小就不可改变。
        int[] ns = new int[5];
        ns[0] = 1;
        // System.out.println(ns.length); // 5
        // 索引超出范围,运行时将报错, eg: ns[5] = 1
        // 编译器自动推算数组大小
        int[] ns2 = new int[]{68, 79, 91, 85, 62};
        int[] ns3 = {68, 79, 91, 85, 62};
        // 遍历数组
        for (int i = 0; i < ns.length; i++) { // 遍历下标
            int tmpN = ns[i];
            // System.out.println(n);
        }
        for (int tmpN : ns) { // 遍历元素
            // System.out.println(tmpN);
        }
        // 多维数组
        int[][] ns4 = {
                {1, 2, 3, 4},
                {5, 6, 7, 8},
                {9, 10, 11, 12}
        };

        // 常量
        // 定义变量的时候,如果加上final修饰符,这个变量就变成了常量
        final double PI = 3.14; // PI是一个常量

        // var关键字
        var sb = new StringBuilder(); // 编译器自动推断 等价于 StringBuilder sb = new StringBuilder();
    }
}

一个Java源文件可以包含多个类的定义,但只能定义一个public类,且public类名必须与文件名一致。如果要定义多个public类,必须拆到多个Java源文件中。

public class Main {
    public static void main(String[] args) {
        var ming1 = new Person();
        ming1.setName("ming1");
        ming1.age = 20;
        // ming1.setAge(-1);
        var ming2 = new Person("ming2", 21);
        // System.out.println(ming1.getName() + ", age: " + ming1.age); // ming1, age: 20
        // System.out.println(ming2.getName() + ", age: " + ming2.age); // ming2, age: 21

        // ming1.hello("zhangsan"); // Hello, zhangsan!
        // ming1.hello("zhangsan", 15); // Hi, zhangsan!!!
        // ming1.hello("zhangsan", 20); // Hello, zhangsan!!!

        // Student > Person > Object
        var stu1 = new Student(10);
        Person stu2 = new Student(10);
        Student stu3 = new Student(10);
        Object stu4 = new Student(10);
        stu1.name = "ABC";
        // System.out.println(stu1.hello()); // Hello, ABC
        // System.out.println(stu1 instanceof Person); // true
        // System.out.println(stu1 instanceof Student); // true
        // stu1.run(); // Student.run
    }
}
//final class Person { // 用final修饰的类不能被继承
class Person {
    // private String idCard; // private 类型的属性和方法,在子类中无法访问到
    // public final String name = "Unamed"; // 用final修饰的字段在初始化后不能被修改, 但可以在构造方法中初始化final字段
    protected String name;
    protected int age;
    protected String gender = "UnKnow"; // 提供默认值
    public static int number; // 静态字段,访问: Person.number

    public static void setNumber(int value) { // 静态方法
        number = value;
    }

    /**
     * 构造函数:构造方法的名称就是类名,没有参数限制
     * 没有在构造方法中初始化字段时,引用类型的字段默认是null,数值类型的字段用默认值,int类型默认值是0,布尔类型默认值是false
     */
    // 类构造方法1
    public Person() {
        // this("NoName", 0); // 构造函数内可以调用其他构造函数
    }

    // 类构造方法2
    public Person(String name, int age) {
        this.name = name;
        // this.age = age;
        this.setAge(age);
    }

    public void setName(String name) {
        if (name == null || name.isBlank()) {
            throw new IllegalArgumentException("invalid name");
        }
        this.name = name;
    }

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

    public void setAge(int age) {
        if (age < 0 || age > 100) {
            throw new IllegalArgumentException("invalid age value");
        }
        this.age = age;
    }

    /**
     * 方法重载 -- 方法名相同,但各自的参数不同, 方法重载的返回值类型通常都是相同的。
     */
    public void hello(String name) {
        System.out.println("Hello, " + name + "!");
    }

    public void hello(String name, int age) {
        if (age < 18) {
            System.out.println("Hi, " + name + "!!!");
        } else {
            System.out.println("Hello, " + name + "!!!");
        }
    }

    // public final String run() { // 用final修饰的方法不能被Override
    public void run() {
        System.out.println("Person.run");
    }
}

/**
 * 继承
 */
class Student extends Person {
    private int score;

    public Student(int score) {
        // 如果用户没有手动调用super, 则编译器自动调用父类的构造方法, 如下:
        // super();
        this.score = score;
    }

    public Student(String name, int age, int score) {
        super(name, age); // 调用父类的构造方法Person(String, int)
        this.score = score;
    }

    public int getScore() {
        return score;
    }

    public void setScore(int score) {
        this.score = score;
    }

    public String hello() {
        // super关键字表示父类(超类)
        return "Hello, " + super.name;
        // return "Hello, " + this.name;
        // return "Hello, " + name;
    }
    // 覆盖父类方法 (Override和Overload不同的是,如果方法签名不同,就是Overload,Overload方法是一个新方法;如果方法签名相同,并且返回值也相同,就是Override。)
    // 注意:方法名相同,方法参数相同,但方法返回值不同,也是不同的方法。在Java程序中,出现这种情况,编译器会报错。
    // 加上@Override可以让编译器帮助检查是否进行了正确的覆写。希望进行覆写,但是不小心写错了方法签名,编译器会报错。 但是@Override不是必需的。
    @Override
    public void run() {
        // super.run(); // 这里也支持调用父类的方法
        System.out.println("Student.run");
    }
}

/**
 * 抽象类Person2 -- 抽象类无法实例化对象
 */
abstract class Person2 {
    public abstract void run();
}
class Student2 extends Person2 {
    // 这里子类必须实现抽象类方法
    @Override
    public void run() {
        System.out.println("Student.run");
    }
}

/**
 * 接口
 * 一个类只能继承自另一个类,不能从多个类继承。但是,一个类可以实现多个interface
 * eg: class Student implements Person, Hello {}
 */
interface Person3 {
    // 接口的静态字段; 因为interface是一个纯抽象类,所以它不能定义实例字段。但是,interface是可以有静态字段的,并且静态字段必须为final类型:
    // public static final int MALE = 1;
    // public static final int FEMALE = 2;
    // 实际上,因为interface的字段只能是public static final类型,所以我们可以把这些修饰符都去掉,上述代码可以简写为:
    // 编译器会自动加上public static final:
    int MALE = 1;
    int FEMALE = 2;

    void run();
    String getName();
}
class Student3 implements Person3 {
    private String name;

    public Student3(String name) {
        this.name = name;
    }

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

    @Override
    public String getName() {
        return this.name;
    }
}

// 一个interface可以继承自另一个interface
interface Hello {
    void hello();
}
interface Person4 extends Hello {
    void run();
    String getName();
}

// default方法
interface Person5 {
    String getName();
    // 接口方法默认实现
    default void run() {
        System.out.println(getName() + " run");
    }
}

参考资料:

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions