Java 学习笔记(基础)

技术学习 / 2021-11-07

Java 学习笔记(基础)

概述

类别

  • Java SE:Java Standard Edition (J2SE)
  • Java ME: Java Mobile Edition (J2ME)
  • Java EE:Java Enterprise Edition (J2EE)

JDK、JRE、JVM

  • JDK:Java Development Kit (Java开发必备)
  • JRE:Java Runtime Environment(Java执行环境)
  • JVM:Java Virtual Machine(Java虚拟机)
  • API :Application Programming Interface(应用编程接口)

程序构造块

  • package:零条或一条,出现在程序最开始的地方

  • import:零条或多条,引入 Java 提供的类(API)

  • class:创建自己的类,公开类的类名跟文件名保持一致

  • main:可执行 Java 程序的入口

    • 三个修饰符:public、static、void
    • 可接收命令行参数
  • 注释

    • 行注释://
    • 块注释:/*……*/
    • 文档注释:/**……*/
      • @author 作者
      • @param 参数
      • @return 返回值
      • @throw 可能引发的异常

执行过程

  • 编译:javac Test.java
  • 执行:java Test (编译生成的 class 字节码文件,命令中没有 .class)

核心语言

语言元素

  • 关键字 50个
  • 标识符
    • 可以是字符、数字、下划线、$,不能以数字开头,不能有 ! 等特殊符号
    • 不能是关键字
    • 大小写敏感
    • 类:首字母大写,如果一个类名由多个单词构成,那么每个单词的首字母都大写, 中间不使用任何的连接符。比如 Person 类,MemberTest 类。
    • 方法:首字母小写。如果一个方法由多个单词构成,那么第一个单词的所有字母全 都小写,从第二个单词开始,每个单词的首字母大写。比如 add,addThreeInt。
    • 属性:命名约定与方法相同。比如 age,ageOfPerson。
    • 包:将公司域名反转作为包名、对于包名 每个字母都需要小写
    • 常量:所有单词的字母都是大写,如果有多个单词, 那么使用下划线连接即可。
  • 运算符
    • 赋值运算符:=
    • 算术运算符:+、-、*、/、%
    • 关系运算符:>、<、>=、<=、!=
    • 短路运算符
      • && 短路与
      • || 短路或
    • 条件运算符:三目运算符 ?:
    • 逻辑运算符:&、|、!
    • 自增 / 自减运算符:++ / –
    • 下标运算符:[]
    • 类型转换运算符:()
    • 其他运算符
      • new
      • instanceof
      • 位运算符
      • 访问成员运算符
  • 字面量(直接量)
    • 整型字面量
      • 100
      • 123L 长整型
    • 实型字面量
      • 9.8F 浮点型
      • 3.2E - 3
    • 字符字面量
      • ‘a’
      • ‘\t’
      • ‘\105’ 大写字母 E
    • 字符串字面量:“hello”
    • 布尔型字面量:true、false
    • 引用字面量:null
    • 类型字面量
      • int.class
      • String.class
  • 分隔符

变量和常量

数据类型

  • 基本数据类型

    • byte: 占用1个字节,取值范围-128 ~ 127

    • short: 占用2个字节,取值范围-215 ~ 215-1

    • int:占用4个字节,取值范围-231 ~ 231-1

    • long:占用8个字节

    • float:占用4个字节

      • 1.1f 字面量才是 float 类型。
    • double:占用8个字节

      • 1.1 字面量属于 double 类型,不能直接将 1.1 直接赋值给 float 变量,因为这是向下转型。Java 不能隐式执行向下转型,因为这会使得精度降低。
    • char: 占用2个字节

    • boolean:占用大小根据实现虚拟机不同有所差异

  • 枚举类型:符号常量

  • 引用类型:对象

  • 包装类型

    • Byte

    • Short

    • Integer

    • Long

    • Float

    • Double

    • Character

    • Boolean

      基本类型都有对应的包装类型,基本类型与其对应的包装类型之间的赋值使用自动装箱与拆箱完成。

      Integer x = 2;     // 装箱
      int y = x;         // 拆箱
      
  • 缓存池

    new Integer(123) 与 Integer.valueOf(123) 的区别在于:

    • new Integer(123) 每次都会新建一个对象

    • Integer.valueOf(123) 会使用缓存池中的对象,多次调用会取得同一个对象的引用。

      Integer x = new Integer(123);
      Integer y = new Integer(123);
      System.out.println(x == y);    // false
      Integer z = Integer.valueOf(123);
      Integer k = Integer.valueOf(123);
      System.out.println(z == k);   // true
      
    • valueOf():

      public static Integer valueOf(int i) {
          if (i >= IntegerCache.low && i <= IntegerCache.high)
              return IntegerCache.cache[i + (-IntegerCache.low)];
          return new Integer(i);
      }
      

字符串

创建字符串对象

  • String str = “hello”; str 引用静态区的字符串字面量
  • String str = new String(“hello”); str 引用堆上的字符串对象
  • 字符串池
    • String s = "aaa";(采用字面值方式赋值)
      • 首先查找 String Pool 中是否存在“aaa”这个对象,如果不存在,则在 String Pool 中创建 一个“aaa”对象,然后将 String Pool 中的这个“aaa”对象的地址返回来,赋给引用变量 s,这样 s 会指向 String Pool 中的这个“aaa”字符串对象
      • 如果存在,则不创建任何对象,直接将 String Pool 中的这个“aaa”对象地址返回来, 赋给 s 引用。
    • String s = new String(“aaa”);
      • 首先在 String Pool 中查找有没有“aaa”这个字符串对象,如果有,则不在 String Pool 中再去创建“aaa”这个对象了,直接在堆中(heap)中创建一个“aaa”字符串对象,然后将堆中的这个“aaa”对象的地址返回来,赋给 s 引用,导致 s 指向了堆中创建的这个“aaa”字符串对象。
      • 如果没有,则首先在 String Pool 中创建一个“aaa“对象,然后再在堆中(heap)创 建一个”aaa“对象,然后将堆中的这个”aaa“对象的地址返回来,赋给 s 引用, 导致 s 指向了堆中所创建的这个”aaa“对象。

操作字符串方法

  • 字符串长度:length()
  • 取字符串:charAt(int)
  • 判断两个字符串内容是否相等:equals()
  • 连接:concat(String)
  • 判断是否以什么开头 / 结尾:startsWith(String); / endsWith(String)
  • 模式匹配:indexOf(String, [int]) / lastIndexOf(String, [int])
  • 取子字符串:substring(int beginIndex, [int endIndex])
  • 修剪字符串左右两边空格:trim()
  • 替换:replace(char oldChar, char newChar); / replaceAll(String regex, String replacement)

数组

一维数组

  • 初始化
    • int[] a = new int[10];
    • int[] a = {1, 2, 3, 4, 5};
  • 常用属性:a.length

二维数组

  • 初始化
    • {% raw %} int[][] a = new int[10][5] {% endraw %};
    • {% raw %} int[][] a = {{1, 2, 3}, {4, 5, 5}, {6, 7, 8}} {% endraw %};
  • 常用属性
    • a.length 多少行
    • a[i].length 多少列

循环结构

三种循环

  • for
  • while
  • do……while

分支结构

  • if……else
  • switch……case……default

面向对象

  • 面向对象程序设计:OOP

    • Object Oriented Programming(简称:OOP):面向对象的程序设计
    • Object Oriented Design,(简称:OOD):面向对象设计
  • 面向对象程序设计的三大基本特征

    • 继承(Inheritence)
      • 对象的一个新类可以从现有的类中派生,派生类可以从它的基类那继承方法和实例变量,且派生类可以修改或新增新的方法使之更适合特殊的需求。
    • 封装(Encapsulation)
      • 将客观事物抽象成类,每个类可以把自身数据和方法只让可信的类或对象操作,对不可信的进行信息隐藏。
    • 多态 (Polymorphism)
      • 允许不同类的对象对同一消息作出响应。不同对象调用相同方法即使参数也相同,最终表现行为是不一样的。
  • 修饰符

    • public: 对所有类可见
    • protected : 对同一包内的类和所有子类可见,不能修饰类
    • default: 默认访问修饰符,在同一包内可见
    • private: 在同一类内可见,不能修饰类
访问级别 访问控制修饰符 同类 同包不同类(不含子类) 同包子类 不同包不同类(不含子类) 不同包子类
公开 public
受保护 protected √(注意)
默认 default
私有 private

封装(Encapsulation)

  • 利用抽象数据类型将数据和基于数据的操作封装在一起,使其构成一个不可分割的独立实体。数据被保护在抽象数据类型的内部,尽可能地隐藏内部的细节,只保留一些对外接口使之与外部发生联系。用户无需知道对象内部的细节,但可以通过对象对外提供的接口来访问该对象。
public class Person {

    private String name;
    private int gender;
    private int age;

    public String getName() {
        return name;
    }

    public String getGender() {
        return gender == 0 ? "man" : "woman";
    }

    public void work() {
        if (18 <= age && age <= 50) {
            System.out.println(name + " is working very hard!");
        } else {
            System.out.println(name + " can't work any more!");
        }
    }
}

继承(Inheritence)

  • 继承实现了 IS-A 关系,例如 Cat 和 Animal 就是一种 IS-A 关系,因此 Cat 可以继承自 Animal,从而获得 Animal 非 private 的属性和方法。

  • 继承应该遵循里氏替换原则,子类对象必须能够替换掉所有父类对象。

  • Cat 可以当做 Animal 来使用,也就是说可以使用 Animal 引用 Cat 对象。父类引用指向子类对象称为 向上转型

    Animal animal = new Cat();
    

多态(Polymorphism)

  • 父类型的引用可以指向子类的对象。

    People people = new Man();
    
  • 当使用多态方式调用方法时,首先检查父类中是否有 sing()方法,如果没有则编译错误;如果有,再去调用子类的 sing()方法。

    Parent p = new Child();
    p.sing();
    
  • 强制类型转换

    • 向上类型转换(upcast):比如说将 Cat 类型转换为 Animal 类型,即将子类型转换为父类型。对于向上类型转换,不需要显式指定。

      Animal animal = new Cat();
      animal.sing();
      
    • 向下类型转换(downcast):比如将 Animal 类型转换为 Cat 类型。即将父类型转换为子类型。对于向下类型转换,必须要显式指定(必须要使用强制类型 转换)。

      Animal animal = new Cat();
      Cat cat = (Cat) animal;
      cat.sing();
      

抽象类与接口

  • 抽象类

    • 抽象类和抽象方法都使用 abstract 关键字进行声明。抽象类一般会包含抽象方法,抽象方法一定位于抽象类中。
    • 抽象类和普通类最大的区别是,抽象类不能被实例化,需要继承抽象类才能实例化其子类。
  • 接口

    • 接口是抽象类的延伸,在 Java 8 之前,它可以看成是一个完全抽象的类,也就是说它不能有任何的方法实现。
    • 从 Java 8 开始,接口也可以拥有默认的方法实现,这是因为不支持默认方法的接口的维护成本太高了。在 Java 8 之前,如果一个接口想要添加新的方法,那么要修改所有实现了该接口的类。
    • 接口的成员(字段 + 方法)默认都是 public 的,并且不允许定义为 private 或者 protected。
    • 接口的字段默认都是 static 和 final 的。
  • 对比

    • 抽象类提供了一种 IS-A 关系,即子类对象必须能够替换掉所有父类对象。接口只是提供一种方法实现契约,并不要求接口和实现接口的类具有 IS-A 关系。

    • 一个类可以实现多个接口,但是不能继承多个抽象类。

    • 接口的字段只能是 static 和 final 类型的,而抽象类的字段没有这种限制。

    • 接口的成员只能是 public 的,而抽象类的成员可以有多种访问权限。

重载与重写

  • 重载

    存在于同一个类中,指一个方法与已经存在的方法名称上相同,但是参数类型、个数、顺序至少有一个不同。

    应该注意的是,返回值不同,其它都相同不算是重载。

  • 重写

    存在于继承体系中,指子类实现了一个与父类在方法声明上完全相同的一个方法。

    • 子类方法的访问权限必须大于等于父类方法;
    • 子类方法的返回类型必须是父类方法返回类型或为其子类型。

Object 类

  • ==

    • 对于原生数据类型来说,比较的是左右两边的值是否相等。
    • 对于引用类型来说,比较左右两边的引用是否指向同一个对象,或者说左右两边的引用地址是否相同。
  • equals

    • 用于检测一个对象是否等于另外一个对象

      public boolean equals(Object obj) {
            return (this == obj);
        }
      
  • 源代码

    package java.lang;
    
    public class Object {
    
        private static native void registerNatives();
        static {
            registerNatives();
        }
    
        public final native Class<?> getClass();
    
        public native int hashCode();
    
        public boolean equals(Object obj) {
            return (this == obj);
        }
    
        protected native Object clone() throws CloneNotSupportedException;
    
        public String toString() {
            return getClass().getName() + "@" + Integer.toHexString(hashCode());
        }
    
        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 {
            if (timeout < 0) {
                throw new IllegalArgumentException("timeout value is negative");
            }
            if (nanos < 0 || nanos > 999999) {
                throw new IllegalArgumentException("nanosecond timeout value out of range");
            }
            if (nanos > 0) {
                timeout++;
            }
            wait(timeout);
        }
    
        public final void wait() throws InterruptedException {
            wait(0);
        }
    
        protected void finalize() throws Throwable { }
    }
    

String

String 被声明为 final,因此它不可被继承。

内部使用 char 数组存储数据,该数组被声明为 final,这意味着 value 数组初始化之后就不能再引用其它数组。并且 String 内部没有改变 value 数组的方法,因此可以保证 String 不可变。

public final class String
    implements java.io.Serializable, Comparable<String>, CharSequence {
    /** The value is used for character storage. */
    private final char value[];

String、StringBuffer、StringBuilder

  • 可变性
    • String 不可变
    • StringBuffer 和 StringBuilder 可变
  • 线程安全
    • String 不可变,因此是线程安全的
    • StringBuilder 不是线程安全的
    • StringBuffer 是线程安全的,内部使用 synchronized 进行同步,由于同步的原因较之 StringBuilder 慢