Java基础知识


一、Java特性

1. 特性

(1) 封装

封装指的是私有化,即隐藏具体属性和实现细节,仅对外开放接口,控制程序中属性的访问级别。

(2) 继承

子类拥有父类的所有属性和方法(除了 private 修饰的属性不能拥有),从而实现了代码的复用。

(3) 多态

对象在不同时刻表现出来的不同状态,最常见的即方法的重载和重写。

2. 接口

  • 接口没有构造方法,不能实例化对象,只能通过接口实现使用。
  • 一个类只能继承一个父类,而一个类却可以实现多个接口。
  • 实现类通过 implements 关键字实现接口类,且必须重写除默认方法外的所有接口方法。
  • 当接口方法声明为 default 时可包含方法体,实现类可选择是否重写,若未重写方法内容在调用时则执行默认方法体内容。
  • 通过 static 可定义静态接口方法,接口方法可包含方法体,实现类无法重写静态接口方法。
public interface TestService {
    /**
     * 普通接口方法,无方法体,实现类必须重写
     */int AddElem(int a, int b);

    /**
     * 默认接口方法,可包含方法体
     */
    default void sayHello() {
    ​    System.out.println("Hello World !");}

    /**
     * 静态接口方法,无法被实现类重写
     */
    static void sayGoodbye() {
        System.out.println("Goodbye.");
    }
}

/**
 * 实现类可以不实现默认方法,但必须重写普通方法
 */
public class TestServiceImpl implements TestService {@Overridepublic int AddElem(int a, int b) {
        return a + b;}
}

public class InterfaceTest {
    public static void main(String[] args) {
    ​    TestService service = new TestServiceImpl();// 执行默认接口方法,打印 Hello World
    ​    service.sayHello();// 通过接口类调用静态接口方法
        TestService.sayGoodbye();}
}

3. 抽象

抽象与接口类似,都是基于对应的共性抽象与多实现,其显著的一个区别在与抽象允许构造函数创建对象,因此可实现一些数据的初始化。

  • 抽象类不能实例化对象 (new),只能通过被继承使用。
  • 抽象方法通过 abstract 声明,没有方法体(同接口方方法)。
  • 抽象类可以不包含抽象方法,但包含抽象方法的类必须声明为抽象类。
  • 继承抽象类的子类必须重写其的抽象方法,或者声明自身为抽象类。
  • 抽象类可以包含普通方法,其子类可选择是否重写,作用与普通类继承一致。
public abstract class AbsFather {
        
    /**
     * 抽象方法,子类必须进行重写或声明为抽象类
     */
    public abstract int AddElem(int a, int b);

    /**
     * 普通方法,子类可选择性重写
     */
    public void hello() {
        System.out.println("Father say hello.");
    }
}
        
/**
  * 子类必须继承父类所以抽象方法,否则必须声明为抽象类
  */
public class AbsSon extends AbsFather {

    /**
     * 重写父类抽象方法
     */
    @Override
    public int AddElem(int a, int b) {
        return a + b;
    }
}

public class AbsTest {
    public static void main(String[] args) {
        // 通过子类实例化
        AbsFather absSon = new AbsSon();
        int sum = absSon.AddElem(10, 20);
        System.out.println(sum);

        // 执行父类 hello() 方法
        absSon.hello();
    }
}

二、基本知识

1. 重载重写

(1) 方法重写

方法重写即当类实现接口或继承父类时,可对父类中的方法进行覆盖重写。

Java 中,如果两个对象在 equals() 方法中相等,那么它们的 hashCode() 值也必须相等。换句话说,两个对象的 hashCode() 值不同,则它们不可能相等,这是因为在常见的如 Map、HashMap 等集合中 hashCode() 值是用于确定对象在哈希表中的位置,以便进行高效的查找和访问。如果两个对象在 equals() 方法中相等,但它们的 hashCode() 值不同,那么它们可能会被放入哈希表中的不同位置,而不是预期的同一位置,这将导致在使用哈希表进行查找和访问时出现问题。因此若需要重写对象的 equlas() 方法时,需要同时重写其的 hashCode() 方法。

在实现 hashCode() 方法时,通常使用对象的属性值来计算一个整数,这个整数应该尽量不与其他对象的 hashCode() 值重复,从而保证不同对象由 hashCode() 计算得到的哈希值一定是不同的。

(2) 方法重载

方法重载的体现方式为方法名相同,但参数的数量或者类型不同,最常见的即类的构造方法。

如下示例中的 User()User(String id, String name) 即方法重载。

public class User {
    private String id;
    private String name;
        
    public User() {
    }

    public User(String id, String name) {
        this.id = id;
        this.name = name;
    }
}

2. 内部类

(1) 匿名类

匿名类 (Anonymous Class) 是一种没有名字的局部类,可以用来实现某个接口或继承某个类,通常用来编写简单的逻辑处理或回调函数。

匿名类的声明方式如下:

new SomeClass() {
    // 实现接口或继承类的方法
};
(2) 内部类

内部类 (Inner Class) 是一种定义在另一个类内部的类,可以被声明为私有、公共或受保护的。与匿名类不同的是,内部类可以有自己的名字和构造函数,而且可以继承其他类或实现接口。

内部类的作用范围可以是定义它的类的方法或代码块内部,也可以是其他类的方法或代码块内部(需要通过外部类的实例来创建内部类的实例)。

内部类的声明方式如下:

class OuterClass {
    // ...
    class InnerClass {
        //...
    }
}

三、关键字

1. this

当实例变量和方法形参重名冲突时,``this` 关键字指代实例变量。

public class Test {

    int num = 1024; 

    public static void main(String[] args) {
        printNum(2048);
    }

    public void printNum(int num) {
        // 输出1024
        System.out.println("Parameter num: " + num);
        // 输出2048
        System.out.println("Instance variable num: " + this.num);
    }
}

2. static

(1) 类变量

static 修饰的变量为类变量,相同类的实例对象的类变量存储于同一内存,任何实例对象对其进行操作都会影响到其它实例对象对其访问。

类变量通常通过 类名.变量名 进行访问,而非实例对象。

public class Test {

    static int staticNum = 4096; 

    public static void main(String[] args) {
        printNum(2048);
    }

    public static void printNum(int num) {
        System.out.println("Parameter num: " + num);
        System.out.println("Parameter num: " + Test.staticNum);
    }
}
(2) 静态代码块

static 除了用于修饰变量和方式之外,还可以以静态代码块的形式独立存在,当 main 执行时会根据静态块出现顺序执行。

需要注意一点,静态块中可以通过 类名.变量名 的形式访问静态变量,但无法直接访问实例变量,只能通过新建对象进行访问。

public class StaticTest {

    private int num1;           // 实例变量
    private static int num2;    // 静态变量

    public static void main(String[] args) {
        // 按静态块出现的先后顺序执行其方法体
    }

    static {
        // 直接赋值 num1 = 10; 是非法的
        StaticTest test = new StaticTest();
        test.num1 = 10;
        System.out.println("静态块 1 : " + test.num1);
    }

    static {
        StaticTest.num2 = 20;
        System.out.println("静态块 2 : " + num2);
    }
}

3. instanceof

通过 instanceof 可以判断变量的类型,需要注意类的子类得到结果仍为 true

class Human { 
    // ...
}

class Student extends Human { 
    // ...
}

public void InstanceDemo() {
    Human human = new Human();
    Student student = new Student();
    System.out.println(student instanceof human);
}

四、数据类型

1. Number

Number 是所有数值类型的父类,当不确定后续将会使用何种类型即可使用 Number 类型。

方法 作用
byteValue() 获取数值的 byte 类型。
intValue() 获取数值的 int 类型。
floatValue() 获取数值的 float 类型。
doubleValue() 获取数值的 double 类型。
longValue() 获取数值的 long 类型。
public void NumberDemo() {
    Number number = 10;
    byte b = number.byteValue();
    System.out.println("byte: " + b);

    int i = number.intValue();
    System.out.println("int: " + i);

    float f = number.floatValue();
    System.out.println("float: " + f);

    double d = number.doubleValue();
    System.out.println("double: " + d);

    long l = number.longValue();
    System.out.println("long: " + l);
}

2. BigDecimal

普通的 int 型数字长度不能超过 11 位,而 BigDecimal 可以处理任意长度的数字序列长度。其相对应的 + - * / 基本运算则由 add(), subtract(), multiply(), divide() 等函数代替。

public void Demo(){
    BigInteger ten = BigInteger.valueOf(10);
    BigInteger twenty = BigInteger.valueOf(20);

    System.out.println(ten.add(twenty));                   // 10 + 20 = 30
    System.out.println(twenty.subtract(ten));              // 20 - 10 = 10
    System.out.println(ten.multiply(twenty));              // 10 * 20 = 200
    System.out.println(twenty.divide(ten));                // 20 / 10 = 2
}

3. String

String 与其它基本类型不同的是其被声明为 final ,即一旦被定义声明则后续无法进行改变。

默认创建的字符串将会存在常量池中,后续若定义内容相同的字符串将从常量池直接引用已存在对象,不会重新创建对象,只有通过 new 关键字才是真正意义的对象创建。

public void demo() {
    /*
     * 保存在字符串常量池中, 如果在后续代码中再次定义字符串“abc”,
     * 那么实际上使用的是之前创建的字符串对象,而不是新创建一个对象。
     */
    String a = "test";
    /*
     * 保存在堆中创建的,而不是保存在字符串常量池中。
     * 每次使用构造函数创建字符串对象时,都会在堆中创建一个新的对象。
     */
    String b = new String("test");
    System.out.println(a == b);
}
(1) 常用方法

字符串常用函数方法如下:

方法 作用
trim() 移除字符串起始与结束的空格字符。
split() 根据指定分隔符将字符串拆分为数组。
join() 根据分隔符将 List 合并为字符串。
replace() 替换字符串的指定字符为特定值。
replaceAll() 替换字符串的所有指定字符为特定值。
public void stringDemo() {
    String origin = "1, 2, 3, 4 ";
    // Remove the head and last empty space
    System.out.println("trim: " + origin.trim());

    // Replace the char in string
    System.out.println("replace: " + origin.replace(" ", ""));

    // Split str to array
    String[] array = origin.split(",");
    System.out.println("split: " + Arrays.toString(array));

    // Merge list to string
    String joinStr = StringUtils.join(Arrays.asList(array), ",");
    System.out.println("join: " + joinStr);
}

4. StringBuilder

StringBuilder 提供一种可变的字符串类型,速度快但非线程安全,适用于单线程。

  • 操作少量的数据, 用 String
  • 单线程操作大量数据,用 StringBuilder (速度快,非线程安全,即异步)。
  • 多线程操作大量数据,用 StringBuffer (通过 synchronized 加锁实现线程安全,但速度慢)。
public void Demo(){
    StringBuilder builder = new StringBuilder();
    builder.append("Hello");
    builder.append("World!");

    // insert a char at index 5
    builder.insert(5, "J");
    // replace a char at index 5
    builder.setCharAt(5, ' ');
    // delete char at specify index
    builder.deleteCharAt(5);

    String result = builder.toString();
    System.out.println(result);
}

五、属性配置

1. 远程调试

在工程的 resources/bootstrap.conf 配置添加如下配置,并在代码中进行加载即可。

java.arg.debug=-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=8000

当然也可以值启动命令中添加 -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=8000 设置远程调试配置。

nohup java -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=8000 -jar my-application.jar> application-info.log 2>&1 &

完成上述配置之后在 IDEA 中启动配置中新增 Remote JVM Debug 并填写对应对应的服务 IP 与端口,同时选择对应的工程模块启动后即可实现远程 Debug

2. 参数配置

当工程中涉及到一些场景可变参数时可以通过外部文件配置属性,再由代码程序进行读取,实现更动态的管理。

在启动项的 VM Option 中添加如下内容,当然实际工程需要添加到对应的配置文件中。

代码中获取配置属性值如下:

public void demo() {
    // Get all property
    Properties property = System.getProperties();
    System.out.println(property);

    // Get specific property
    String info = System.getProperty("properties.info");
    System.out.println(info);
}

文章作者: 烽火戏诸诸诸侯
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 烽火戏诸诸诸侯 !
  目录