Java-02: Basic

数据类型

基本数据类型

  • 整数类型:byte/8, short/16, int/32, long/64
  • 浮点类型:float/32, double/64
  • 字符类型:char/16
  • 布尔类型:boolean/1

包装类型

包装类型让基本数据类型拥有了对象的特性,方便它们参与到面向对象的编程中,尤其是在集合框架、泛型以及需要表示 null 值时。

java
Integer i = 10;  // 装箱
int j = i;  // 拆箱

// 集合
ArrayList<Integer> numbers = new ArrayList<>(); // 不能使用ArrayList<int>
numbers.add(1);

// 泛型
Map<String, Integer> map = new HashMap<>(); // 键值对都要使用包装类型

// 方法参数
public void print(Integer i) {
    System.out.println(i);
}

// 返回值
public Integer add(int a, int b) {
    return a + b;
}

// 表示 null 值
Integer i = null;

// 与字符串互相转换
String str = "123";
Integer i = Integer.parseInt(str);
String str2 = i.toString();

// 网络编程和IO操作
Socket socket = new Socket("localhost", 8080);
File file = new File("test.txt");
FileInputStream fis = new FileInputStream(file);
FileOutputStream fos = new FileOutputStream(file);

// ORM
User user = userDao.findById(1);

缓存池

  • 缓存池:为了优化性能和节省内存,Java 为一些常用的值(如 -128 到 127)提供了一个缓存池,当需要这些值时,可以直接从缓存池中获取,而不是重新创建。

  • 缓存池范围:

    • Byte、Short、Integer、Long:默认缓存范围是 -128 到 127。
    • Character:缓存范围是 0 到 127(ASCII 码)。
    • Boolean:缓存 true 和 false 两个实例。
java
public class CachePoolExample {
    public static void main(String[] args) {
        // Integer 缓存池示例
        Integer i1 = 100; // 自动装箱,100在-128到127范围内
        Integer i2 = 100; // 自动装箱,从缓存池中获取
        Integer i3 = 200; // 自动装箱,200超出-128到127范围,会创建新对象
        Integer i4 = 200; // 自动装箱,200超出范围,会创建新对象
        System.out.println("i1 == i2: " + (i1 == i2)); // true,因为都是从缓存池中获取的同一个对象
        System.out.println("i3 == i4: " + (i3 == i4)); // false,因为创建了两个不同的对象
        // Boolean 缓存池示例
        Boolean b1 = true;
        Boolean b2 = true;
        System.out.println("b1 == b2: " + (b1 == b2)); // true,Boolean缓存了true和false
        // Character 缓存池示例
        Character c1 = 'a'; // 'a'的ASCII码是97,在0到127范围内
        Character c2 = 'a';
        Character c3 = ''; // '中'的ASCII码超过127
        Character c4 = '';
        System.out.println("c1 == c2: " + (c1 == c2)); // true
        System.out.println("c3 == c4: " + (c3 == c4)); // false
        // 正确比较包装类型的值应该使用 equals() 方法
        System.out.println("i3.equals(i4): " + i3.equals(i4)); // true
    }
}
  • new Integer(123) 与 Integer.valueOf(123) 的区别: new Integer(123) 会创建一个新的对象,而 Integer.valueOf(123) 会从缓存池中获取一个对象

    java
    Integer i1 = new Integer(123);
    Integer i2 = Integer.valueOf(123);
    
    System.out.println(i1 == i2);  // false
    Integer i3 = Integer.valueOf(123);
    Integer i4 = Integer.valueOf(123);
    System.out.println(i3 == i4);  // true

String

java
public final class String       // 不可继承
    implements java.io.Serializable, Comparable<String>, CharSequence {
    private final char value[];     // 不可变字符序列
    }

不可变性

  • 优点:
    1. 可以缓存 hash 值
    2. String Pool 的需要
    3. 安全性
    4. 线程安全

StringBuffer 和 StringBuilder

  • StringBuffer 是线程安全的,通过 synchronized 关键字实现,StringBuilder 是线程不安全的
  • StringBuffer 的性能比 StringBuilder 差
  • StringBuffer 的 API 比 StringBuilder 多

String.intern()

  • 将字符串常量池中的字符串添加到 String Pool 中
  • 如果 String Pool 中已经存在该字符串,则返回 String Pool 中的字符串
  • 如果 String Pool 中不存在该字符串,则将该字符串添加到 String Pool 中,并返回 String Pool 中的字符串
java
String s1 = "hello";
String s2 = new String("hello");
String s3 = s2.intern();
String s4 = "hello";
System.out.println(s1 == s2); // false
System.out.println(s1 == s3); // true
System.out.println(s1 == s4); // true

String.format()

  • 格式化字符串
java
String s = String.format("Hello %s", "World");
System.out.println(s); // Hello World

运算

参数传递

  • java 参数传递是使用的值传递,student 存储的是对象地址,传入 func 方法时,传递的是对象地址的副本,因此在方法中改变指针引用的对象,那么两个指针将指向不同的对象,因此不会改变原始对象。

    java
    public class Student {
        name;
    
        Student(String name) {
            this.name = name;
        }
    
        String getName() {
            return this.name;
        }
    
        void setName(String name) {
            this.name = name;
        }
    }
    
    public class PassByValueExample {
        public static void main(String[] args) {
            Student student = new Student("A");
            System.out.println(student.getName()); // A
            func(student);
            System.out.println(student.getName()); // A
        }
    
        private static void func(Student student) {
            System.out.println(student.getName()); // A
            Student student = new Student("B");
            System.out.println(student.getName()); // B
        }
    }
  • 但是如果改变的是对象的属性,那么就会改变原始对象,因为修改的是同一地址指向的内容。

    java
      class PassByValueExample {
          public static void main(String[] args) {
              Student student = new Student("A");
              System.out.println(student.getName()); // A
              func(student);
              System.out.println(student.getName()); // B
      }
    
      private static void func(Student student) {
          System.out.println(student.getName()); // A
          student.setName("B");
          System.out.println(student.getName()); // B
      }
    }

float 和 double

  • 1.1 字面量属于 double 类型,不能将 1.1 直接复制给 float,因为这是向下转型,java 不支持隐式向下转型。

  • 1.1f 才是 floatl 类型

    java
    float f = 1.1f

隐式类型转换

  • 字面量 1 是 int 类型,所以不能隐式转型为 short

    java
    short s = 1;
    s = s + 1; // 错误
  • 但是可以使用+=运算符,因为+=运算符会进行隐式类型转换

    java
    short s = 1;
    s += 1; // 正确
    // 相当于
    s = (short) (s + 1);

switch

java
switch (expression) {
    case value1:
        // code block
        break;
    case value2:
        // code block
        break;
}

访问权限

  • 修饰符:public、protected、private,如果不加修饰符,表示默认访问权限,即包内可见。

  • 类可见,表示该类可以被其他类用于创建对象。

  • 成员可见,表示该成员可以被其他类访问。

  • protected 表示该成员对于子类可见,但是对于类没有意义。

  • 子类的访问级别不能高于父类的访问级别。

  • 字段绝对不能是公有的,因为这样会丢失字段修改行为的控制。通常使用 private 修饰,并提供 public 的 getter 和 setter 方法。

    java
    public class Student {
        private int id;
    
        public int getId() {
            return id + "";
        }
    
        public void setId(String id) {
            this.id = Integer.valueOf(id);
        }
    }

抽象类和接口

  • 抽象类:抽象类不能被实例化,但是可以被继承。使用 abstract 修饰。

    java
    public abstract class Animal {
    
      public abstract void eat();
    
      public void sleep() {
          System.out.println("sleep");
      }
    }
    
    public class Dog extends Animal {
      @Override
      public void eat() {
          System.out.println("eat");
      }
    }
    
    Animal animal = new Dog();
    animal.eat();
  • 接口:接口不能被实例化,但是可以被实现。使用 interface 修饰。接口中的字段和方法默认是 public abstract 的。

    java
    public interface Animal {
      void eat();
    }
    java
    public class Dog implements Animal {
      @Override
      public void eat() {
          System.out.println("eat");
      }
    }
    
    Animal animal = new Dog();
    animal.eat();

比较

区别抽象类接口
关系IS-A(里氏替换原则)LIKE-A
实现单继承多继承
字段没有限制static final
成员权限没有限制public abstract
方法可以有实现只能有抽象方法
构造函数可以有构造函数不能有构造函数

选择(接口优先级高于抽象类)

  • 接口:1.不相关的类都实现一些方法,2.使用多重继承

  • 抽象类:1.相关类共享代码 2.需要能控制继承来的成员的访问权限 3.需要继承非静态和非常量字段。

Super

  • 使用 super 调用父类的构造函数,委托父类完成初始化工作

    java
    public class Animal {
      protected int age;
    
      public Animal(int age) {
          this.age = age;
      }
    
      public void eat() {
          System.out.println("eat");
      }
    }
    
    public class Dog extends Animal {
      private String name;
    
      public Dog(int age, String name) {
          super(age);
          this.name = name;
      }
    
      @Override
      public void eat() {
          super.eat();
          System.out.println("dog eat")
      }
    }
    
    Dog dog = new Dog(1);
    dog.eat(); // eat dog eat

重写与重载

重写(使用@Override注解)

  • 子类方法访问权限不能低于父类方法访问权限

  • 子类方法返回类型必须是父类方法返回类型的子类

重载

  • 存在同一个类,方法名相同,参数类型、个数、顺序至少一个不同

  • 重载与返回值无关,只与方法签名有关

通用方法

java
public final native Class<?> getClass() // 获取对象的类

public native int hashCode() // 获取对象的哈希码

public boolean equals(Object obj) // 判断对象是否相等:自反性、对称性、传递性、一致性、非空性

protected native Object clone() throws CloneNotSupportedException // 克隆对象

public String toString() // 返回对象的字符串表示

public final native void notify() // 唤醒等待的线程

public final native void notifyAll() // 唤醒所有等待的线程

public final native void wait(long timeout) throws InterruptedException // 等待指定时间

public final void wait(long timeout, int nanos) throws InterruptedException // 等待指定时间

public final void wait() throws InterruptedException // 等待

protected void finalize() throws Throwable {} // 垃圾回收
  • 浅拷贝:拷贝对象的引用,如果对象的属性是基本类型,则拷贝值,如果对象的属性是引用类型,则拷贝引用。

    java
    public class ShallowCopy {
      public static void main(String[] args) {
          Student student = new Student("A");
          Student student2 = student;
          student2.setName("B");
          System.out.println(student.getName()); // B
      }
    }
  • 深拷贝:拷贝对象和原始对象的引用类型引用不同的对象。

    java
    public class Student implements Cloneable {
      private int id;
    
      public Student(int id) {
          this.id = id;
      }
    
      public void setId(int id) {
          this.id = id;
      }
    
      public int getId() {
          return id;
      }
    
      @Override
      protected Object clone() throws CloneNotSupportedException {
          Student student = (Student) super.clone();
          student.id = this.id;
          return student;
      }
    }
    
    Student student = new Student(1);
    Student student2 = student.clone();
    student2.setId(2);
    System.out.println(student.getId()); // 1
    System.out.println(student2.getId()); // 2
  • clone的替代,拷贝构造函数或者拷贝工厂

    java
    public class Student {
      private int id;
    
      public Student(int id) {
          this.id = id;
      }
    
      public Student(Student student) {
          this.id = student.id;
      }
    
      public void setId(int id) {
          this.id = id;
      }
    
      public int getId() {
          return id;
      }
    }
    
    public class StudentFactory {
      public static Student createStudent(int id) {
          return new Student(id);
      }
    }
    
    Student student = StudentFactory.createStudent(1);
    System.out.println(student.getId()); // 1

关键字

  • final:

    • 修饰类:类不能被继承

    • 修饰方法:方法不能被重写

    • 修饰变量:变量不能被修改

    • 修饰引用:引用不能被修改,但是引用指向的对象可以被修改

  • static

    • 静态变量:也称类变量,属于类,不属于对象,所有对象共享

    • 实例变量:也称成员变量,属于对象,每个对象都有一份

    java
    public class Student {
      private int id; // 实例变量
      private static int count = 0; // 静态变量
    
      public Student(int id) {
          this.id = id;
          count++;
      }
    }
    • 静态方法:类加载的时候就存在,不需要实例化就可以调用,必须有实现,不能是抽象方法。且只能访问静态变量和静态方法,不能有this和super关键字。

    • 静态代码块:在类初始化的时候运行一次。

    • 静态内部类:非静态内部类依赖于外部类,而静态内部类不需要依赖外部类,可以独立存在。但是只能访问外部类的非静态变量和方法。

    • 静态导包:不再指明包名,直接使用静态方法和变量,但是可读性较差。

    • 初始化顺序:静态变量和静态语句块优先于实例变量和普通语句块,静态变量和静态语句块的初始化顺序取决于它们在代码中的顺序。最后才是构造函数的初始化。

    • 存在继承关系时,初始化顺序为:

      1. 父类静态变量和静态语句块
      2. 子类静态变量和静态语句块
      3. 父类实例变量和普通语句块
      4. 父类构造函数
      5. 子类实例变量和普通语句块
      6. 子类构造函数

反射

  • 每一个类都有Class对象,Class对象保存了类的一些信息,如类名、方法、字段等。当编译一个类时,会生成一个Class对象,并保存在.class文件中。类加载相当于class对象的加载,类再第一次使用的似乎才会动态加载到JVM中,可以使用Class.forName("com.example.Student")来控制加载时机,该方法会返回与参数中指定的对象相应的Class对象的引用。

  • Class和java.lang.reflect提供了反射的支持,java.lang.reflect包提供了Field、Method和Constructor类,可以用来获取类的信息。

    • Field:表示类的成员变量,可以使用get和set方法来获取和设置字段的值。

    • Method:表示类的方法,可以使用invoke方法来调用方法。

    • Constructor:表示类的构造函数,可以使用newInstance方法来创建对象。

异常

  • 受检异常:用try-catch来处理,并从异常中恢复。

  • 非受检异常:程序运行时错误,程序崩溃。

泛型

  • 泛型类:使用<T>来表示泛型,T可以表示任何类型。

    java
    public class Generic<T> {
      private T t;
    
      public T getT() {
          return t;
      }
    
      public void setT(T t) {
          this.t = t;
      }
    }

JRE和JDK

  • JRE:Java Runtime Environment,Java运行环境,包含了JVM和Java核心类库。

  • JDK:Java Development Kit,Java开发工具包,包含了JRE和开发工具。

参考链接