我肝了一个月,给你写出了这本Java开发手册。(四)

简介: 先来看一下本篇文章的思维导图吧,我会围绕下面这些内容进行讲解。内容很干,小伙伴们看完还希望不吝转发。(高清思维导图版本关注作者公众号 Java建设者 回复 Java666 获取,其他思维导图获取方式在文末)。

初始化

类的初始化

上面我们创建出来了一个 Car 这个对象,其实在使用 new 关键字创建一个对象的时候,其实是调用了这个对象无参数的构造方法进行的初始化,也就是如下这段代码

class Car{
  public Car(){}
}

这个无参数的构造函数可以隐藏,由 JVM 自动添加。也就是说,构造函数能够确保类的初始化。

成员初始化

Java 会尽量保证每个变量在使用前都会获得初始化,初始化涉及两种初始化。

  • 一种是编译器默认指定的字段初始化,基本数据类型的初始化

微信图片_20220414185839.png

  • 一种是其他对象类型的初始化,String 也是一种对象,对象的初始值都为 null ,其中也包括基本类型的包装类。
  • 一种是指定数值的初始化,例如
int a = 11

也就是说, 指定 a 的初始化值不是 0 ,而是 11。其他基本类型和对象类型也是一样的。

构造器初始化

可以利用构造器来对某些方法和某些动作进行初始化,确定初始值,例如

public class Counter{
  int i;
  public Counter(){
    i = 11;
  }
}

利用构造函数,能够把 i 的值初始化为 11。

初始化顺序

首先先来看一下有哪些需要探讨的初始化顺序

  • 静态属性:static 开头定义的属性
  • 静态方法块:static {} 包起来的代码块
  • 普通属性:非 static 定义的属性
  • 普通方法块:{} 包起来的代码块
  • 构造函数:类名相同的方法
  • 方法:普通方法
public class LifeCycle {
    // 静态属性
    private static String staticField = getStaticField();
    // 静态方法块
    static {
        System.out.println(staticField);
        System.out.println("静态方法块初始化");
    }
    // 普通属性
    private String field = getField();
    // 普通方法块
    {
        System.out.println(field);
    }
    // 构造函数
    public LifeCycle() {
        System.out.println("构造函数初始化");
    }
    public static String getStaticField() {
        String statiFiled = "Static Field Initial";
        return statiFiled;
    }
    public static String getField() {
        String filed = "Field Initial";
        return filed;
    }   
    // 主函数
    public static void main(String[] argc) {
        new LifeCycle();
    }
}

这段代码的执行结果就反应了它的初始化顺序

静态属性初始化 静态方法块初始化 普通属性初始化 普通方法块初始化 构造函数初始化

数组初始化

数组是相同类型的、用一个标识符名称封装到一起的一个对象序列或基本类型数据序列。数组是通过方括号下标操作符 [] 来定义使用。

一般数组是这么定义的

int[] a1;
//或者
int a1[];

两种格式的含义是一样的。

  • 直接给每个元素赋值 : int array[4] = {1,2,3,4};
  • 给一部分赋值,后面的都为 0 :int array[4] = {1,2};
  • 由赋值参数个数决定数组的个数 :int array[] = {1,2};

可变参数列表

Java 中一种数组冷门的用法就是可变参数 ,可变参数的定义如下

public int add(int... numbers){
  int sum = 0;
  for(int num : numbers){
    sum += num;
  }
  return sum;
}

然后,你可以使用下面这几种方式进行可变参数的调用

add();  // 不传参数
add(1);  // 传递一个参数
add(2,1);  // 传递多个参数
add(new Integer[] {1, 3, 2});  // 传递数组

对象的销毁

虽然 Java 语言是基于 C++ 的,但是它和 C/C++ 一个重要的特征就是不需要手动管理对象的销毁工作。在著名的一书 《深入理解 Java 虚拟机》中提到一个观点

微信图片_20220414185848.png

在 Java 中,我们不再需要手动管理对象的销毁,它是由 Java 虚拟机进行管理和销毁的。虽然我们不需要手动管理对象,但是你需要知道 对象作用域 这个概念。

对象作用域

J多数语言都有作用域(scope) 这个概念。作用域决定了其内部定义的变量名的可见性和生命周期。在 C、C++ 和 Java 中,作用域通常由 {} 的位置来决定,例如

{
  int a = 11;
  {
    int b = 12;
  }
}

a 变量会在两个 {} 作用域内有效,而 b 变量的值只能在它自己的 {} 内有效。

虽然存在作用域,但是不允许这样写

{
  int x = 11;
  {
    int x = 12;
  }
}

这种写法在 C/C++ 中是可以的,但是在 Java 中不允许这样写,因为 Java 设计者认为这样写会导致程序混乱。

###this 和 super

this 和 super 都是 Java 中的关键字

this 表示的当前对象,this 可以调用方法、调用属性和指向对象本身。this 在 Java 中的使用一般有三种:指向当前对象

public class Apple {
    int i = 0;
    Apple eatApple(){
        i++;
        return this;
    }
    public static void main(String[] args) {
        Apple apple = new Apple();
        apple.eatApple().eatApple();
    }
}

这段代码比较精妙,精妙在哪呢,我一个 eatApple() 方法竟然可以调用多次,你在后面还可以继续调用,这就很神奇了,为啥呢?其实就是 this 在作祟了,我在 eatApple 方法中加了一个 return this 的返回值,也就是说哪个对象调用 eatApple 方法都能返回对象的自身。

this 还可以修饰属性,最常见的就是在构造方法中使用 this ,如下所示

public class Apple {
    private int num;
    public Apple(int num){
        this.num = num;
    }
    public static void main(String[] args) {
        new Apple(10);
    }
}

main 方法中传递了一个 int 值为 10 的参数,它表示的就是苹果的数量,并把这个数量赋给了 num 全局变量。所以 num 的值现在就是 10。

this 还可以和构造函数一起使用,充当一个全局关键字的效果

public class Apple {
    private int num;
    private String color;
    public Apple(int num){
        this(num,"红色");
    }
    public Apple(String color){
        this(1,color);
    }
    public Apple(int num, String color) {
        this.num = num;
        this.color = color;
    }
}

你会发现上面这段代码使用的不是 this, 而是 this(参数)。它相当于调用了其他构造方法,然后传递参数进去。这里注意一点:this() 必须放在构造方法的第一行,否则编译不通过

微信图片_20220414185853.png

如果你把 this 理解为指向自身的一个引用,那么 super 就是指向父类的一个引用。super 关键字和 this 一样,你可以使用 super.对象 来引用父类的成员,如下

public class Fruit {
    int num;
    String color;
    public void eat(){
        System.out.println("eat Fruit");
    }
}
public class Apple extends Fruit{
    @Override
    public void eat() {
        super.num = 10;
        System.out.println("eat " + num + " Apple");
    }
}

你也可以使用 super(参数) 来调用父类的构造函数,这里不再举例子了。

下面为你汇总了 this 关键字和 super 关键字的比较。

微信图片_20220414185857.png

访问控制权限

访问控制权限又称为封装,它是面向对象三大特性中的一种,我之前在学习过程中经常会忽略封装,心想这不就是一个访问修饰符么,怎么就是三大特性的必要条件了?后来我才知道,如果你信任的下属对你隐瞒 bug,你是根本不知道的

访问控制权限其实最核心就是一点:只对需要的类可见。

Java中成员的访问权限共有四种,分别是 public、protected、default、private,它们的可见性如下

微信图片_20220414185901.png

继承

继承是所有 OOP(Object Oriented Programming) 语言和 Java 语言都不可或缺的一部分。只要我们创建了一个类,就隐式的继承自 Object 父类,只不过没有指定。如果你显示指定了父类,那么你继承于父类,而你的父类继承于 Object 类。

微信图片_20220414185908.png

继承的关键字是 extends ,如上图所示,如果使用了 extends 显示指定了继承,那么我们可以说 Father 是父类,而 Son 是子类,用代码表示如下

class Father{}
class Son extends Father{}

继承双方拥有某种共性的特征

class Father{
  public void feature(){
    System.out.println("父亲的特征");
  }
}
class Son extends Father {
}

如果 Son 没有实现自己的方法的话,那么默认就是用的是父类的 feature 方法。如果子类实现了自己的 feature 方法,那么就相当于是重写了父类的 feature 方法,这也是我们上面提到的重写了。

多态

多态指的是同一个行为具有多个不同表现形式。是指一个类实例(对象)的相同方法在不同情形下具有不同表现形式。封装和继承是多态的基础,也就是说,多态只是一种表现形式而已。

如何实现多态?多态的实现具有三种充要条件

  • 继承
  • 重写父类方法
  • 父类引用指向子类对象

比如下面这段代码

public class Fruit {
    int num;
    public void eat(){
        System.out.println("eat Fruit");
    }
}
public class Apple extends Fruit{
    @Override
    public void eat() {
        super.num = 10;
        System.out.println("eat " + num + " Apple");
    }
    public static void main(String[] args) {
        Fruit fruit = new Apple();
        fruit.eat();
    }
}

你可以发现 main 方法中有一个很神奇的地方,Fruit fruit = new Apple(),Fruit 类型的对象竟然指向了 Apple 对象的引用,这其实就是多态 -> 父类引用指向子类对象,因为 Apple 继承于 Fruit,并且重写了 eat 方法,所以能够表现出来多种状态的形式。

组合

组合其实不难理解,就是将对象引用置于新类中即可。组合也是一种提高类的复用性的一种方式。如果你想让类具有更多的扩展功能,你需要记住一句话多用组合,少用继承

public class SoccerPlayer {
    private String name;
    private Soccer soccer;
}
public class Soccer {
    private String soccerName;    
}

代码中 SoccerPlayer 引用了 Soccer 类,通过引用 Soccer 类,来达到调用 soccer 中的属性和方法。

组合和继承是有区别的,它们的主要区别如下。

关于继承和组合孰优孰劣的争论没有结果,只要发挥各自的长处和优点即可,一般情况下,组合和继承也是一对可以连用的好兄弟。

代理

除了继承和组合外,另外一种值得探讨的关系模型称为 代理。代理的大致描述是,A 想要调用 B 类的方法,A 不直接调用,A 会在自己的类中创建一个 B 对象的代理,再由代理调用 B 的方法。例如如下代码

public class Destination {
    public void todo(){
        System.out.println("control...");
    }
}
public class Device {
    private String name;
    private Destination destination;
    private DeviceController deviceController;
    public void control(Destination destination){
        destination.todo();
    }
}
public class DeviceController {
    private Device name;
    private Destination destination;
    public void control(Destination destination){
        destination.todo();
    }
}

向上转型

向上转型代表了父类与子类之间的关系,其实父类和子类之间不仅仅有向上转型,还有向下转型,它们的转型后的范围不一样

  • 向上转型:通过子类对象(小范围)转化为父类对象(大范围),这种转换是自动完成的,不用强制。
  • 向下转型 : 通过父类对象(大范围)实例化子类对象(小范围),这种转换不是自动完成的,需要强制指定。

static

static 是 Java 中的关键字,它的意思是 静态的,static 可以用来修饰成员变量和方法,static 用在没有创建对象的情况下调用 方法/变量。

  • 用 static 声明的成员变量为静态成员变量,也成为类变量。类变量的生命周期和类相同,在整个应用程序执行期间都有效。
static String name = "cxuan";
  • 使用 static 修饰的方法称为静态方法,静态方法能够直接使用类名.方法名 进行调用。由于静态方法不依赖于任何对象就可以直接访问,因此对于静态方法来说,是没有 this 关键字的,实例变量都会有 this 关键字。在静态方法中不能访问类的非静态成员变量和非静态方法,
static void printMessage(){
  System.out.println("cxuan is writing the article");
}

static 除了修饰属性和方法外,还有静态代码块 的功能,可用于类的初始化操作。进而提升程序的性能。

public class StaicBlock {
    static{
        System.out.println("I'm A static code block");
    }
}

由于静态代码块随着类的加载而执行,因此,很多时候会将只需要进行一次的初始化操作放在 static 代码块中进行。

final

final 的意思是最后的、最终的,它可以修饰类、属性和方法。

  • final 修饰类时,表明这个类不能被继承。final 类中的成员变量可以根据需要设为 final,但是要注意 final 类中的所有成员方法都会被隐式地指定为 final 方法。
  • final 修饰方法时,表明这个方法不能被任何子类重写,因此,如果只有在想明确禁止该方法在子类中被覆盖的情况下才将方法设置为 final。
  • final 修饰变量分为两种情况,一种是修饰基本数据类型,表示数据类型的值不能被修改;一种是修饰引用类型,表示对其初始化之后便不能再让其指向另一个对象。
相关文章
|
30天前
|
Java
Java开发实现图片URL地址检验,如何编码?
【10月更文挑战第14天】Java开发实现图片URL地址检验,如何编码?
62 4
|
29天前
|
监控 Java 测试技术
Java开发现在比较缺少什么工具?
【10月更文挑战第15天】Java开发现在比较缺少什么工具?
34 1
|
30天前
|
Java
Java开发实现图片地址检验,如果无法找到资源则使用默认图片,如何编码?
【10月更文挑战第14天】Java开发实现图片地址检验,如果无法找到资源则使用默认图片,如何编码?
56 2
|
10天前
|
SQL 安全 Java
安全问题已经成为软件开发中不可忽视的重要议题。对于使用Java语言开发的应用程序来说,安全性更是至关重要
在当今网络环境下,Java应用的安全性至关重要。本文深入探讨了Java安全编程的最佳实践,包括代码审查、输入验证、输出编码、访问控制和加密技术等,帮助开发者构建安全可靠的应用。通过掌握相关技术和工具,开发者可以有效防范安全威胁,确保应用的安全性。
23 4
|
12天前
|
缓存 监控 Java
如何运用JAVA开发API接口?
本文详细介绍了如何使用Java开发API接口,涵盖创建、实现、测试和部署接口的关键步骤。同时,讨论了接口的安全性设计和设计原则,帮助开发者构建高效、安全、易于维护的API接口。
35 4
|
22天前
|
开发框架 JavaScript 前端开发
HarmonyOS UI开发:掌握ArkUI(包括Java UI和JS UI)进行界面开发
【10月更文挑战第22天】随着科技发展,操作系统呈现多元化趋势。华为推出的HarmonyOS以其全场景、多设备特性备受关注。本文介绍HarmonyOS的UI开发框架ArkUI,探讨Java UI和JS UI两种开发方式。Java UI适合复杂界面开发,性能较高;JS UI适合快速开发简单界面,跨平台性好。掌握ArkUI可高效打造符合用户需求的界面。
74 8
|
17天前
|
SQL Java 程序员
倍增 Java 程序员的开发效率
应用计算困境:Java 作为主流开发语言,在数据处理方面存在复杂度高的问题,而 SQL 虽然简洁但受限于数据库架构。SPL(Structured Process Language)是一种纯 Java 开发的数据处理语言,结合了 Java 的架构灵活性和 SQL 的简洁性。SPL 提供简洁的语法、完善的计算能力、高效的 IDE、大数据支持、与 Java 应用无缝集成以及开放性和热切换特性,能够大幅提升开发效率和性能。
|
18天前
|
存储 Java 关系型数据库
在Java开发中,数据库连接是应用与数据交互的关键环节。本文通过案例分析,深入探讨Java连接池的原理与最佳实践
在Java开发中,数据库连接是应用与数据交互的关键环节。本文通过案例分析,深入探讨Java连接池的原理与最佳实践,包括连接创建、分配、复用和释放等操作,并通过电商应用实例展示了如何选择合适的连接池库(如HikariCP)和配置参数,实现高效、稳定的数据库连接管理。
35 2
|
18天前
|
监控 Java 数据库连接
在Java开发中,数据库连接管理是关键问题之一
在Java开发中,数据库连接管理是关键问题之一。本文介绍了连接池技术如何通过预创建和管理数据库连接,提高数据库操作的性能和稳定性,减少资源消耗,并简化连接管理。通过示例代码展示了HikariCP连接池的实际应用。
18 1
|
26天前
|
Java 大数据 API
别死脑筋,赶紧学起来!Java之Steam() API 常用方法使用,让开发简单起来!
分享Java Stream API的常用方法,让开发更简单。涵盖filter、map、sorted等操作,提高代码效率与可读性。关注公众号,了解更多技术内容。