《C++转java快速入手系列》类与对象篇

文章目录

《C++转java快速入手系列》类与对象篇

1、类与对象

Java中的类与对象与C++中的意义相同,只是有些地方的用法做了修改

1.1、类的定义格式

在Java中类的定义只能使用关键字class ,没有将C语言中的结构体留存下来

  • 在类中的一些字段被称作成员变量 或者类的属性
  • 在类中的方法被称为成员方法

1.2、类的实例化

  • 在Java中,自定义类型也属于引用类型 ,所以在实例化出具体对象时也必须要用new
java 复制代码
class Student{
    String name;
    int age;
    int sn;
}
public class Test {
    public static void main(String[] args) {
        Student student = new Student();
    }
}

2、this引用

Java中this引用与C++中的this指针作用相同,都是在类中的成员方法中为了能够不显示传入成员变量 的情况下调用成员变量

  • 通常情况下,this引用可以省略,但是当成员方法中的成员变量名传入的参数名相同时,必须要加this引用
  • this引用是指向当前对象的,所以只有在实例化成具体的对象后才会出现,类中是没有this引用的
  • this只能在"成员方法"中使用
  • 在"成员方法"中,this只能引用当前对象,不能再引用其他对象

3、对象的初始化与构造函数

在 Java 中,new 关键字负责分配内存并清零;之后,显式初始化初始化块 以及构造函数体 会按顺序执行,共同完成对象的初始化。其中构造函数只负责 程序员额外编写的初始化逻辑,并非所有初始化都由它完成

接下来我们要区分一些在C++ 和java中名称相同底层功能有差异的地方

  • 在初始化中,首先会在类当中找有没有现成的好的构造函数 ,如果没有则自动生成对应的默认构造
  • 在java中,因为函数传参中没有缺省值这一说,所以默认构造只有一种,那就是无参数类型的构造函数
  • 在java中自动生成的默认构造并没有初始化的功能,它的存在只是为了能够用new实例化对象
  • 构造方法中,可以通过this调用其他构造方法来简化代码,注意this(...)必须是构造方法中第一条语句

实例化对象中,进行初始化的位置可以有三种(除了jvm最先开始赋予的默认值)

  1. 字段中 就地初始化
java 复制代码
public class Demo {
    int a = 10;           // 显式赋值
    String s = "hello";   // 显式赋值
}
  1. 构造体中
java 复制代码
public class Demo {
    int a = 1;          // ① 先执行
    { a = 2; }         // ② 再执行

    public Demo() {
        a = 3;          // ③ 最后执行
    }
}
class Parent {
    int p = 10;
    Parent() {
        p = 20;  // 父类初始化
    }
}
class Child extends Parent {
    int c = 100;
    Child() {
        this(200);  // 委托给另一个构造器
    }
    Child(int val) {
        c = val;    // 最终执行此处
    }
}
  1. 实例初始化块(非静态代码块)
java 复制代码
public class Demo {
    int a;
    {
        a = 5;      // 初始化块
        System.out.println("初始化块执行");
    }
}

4、封装

面向对象程序 三大特性:封装、继承、多态

封装的核心是将数据和操作数据的方法组合成一个整体(即"类")。

通过访问修饰符(如 private、public)来限制对内部数据的直接访问。

4.1、封装扩展之包

在java中要想清晰的分辨出访问限定符具体的控制范围,那我们应该先要了解这个概念

  • 在面向对象体系中,提出了一个软件包的概念,即:为了更好的管理类,把多个类收集在一起成为一组,称为软件包

  • 包最直观的作用 之一就是,在一个工程中可以存在相同名字的类 ,只要它们不处在同一个包中

  • 一般我们想要在某些类中导入包用import 语句

  • 还可以使用import static 导入包中静态的方法和字段

4.1.1、自定义包

基本规则 :

  • 在文件的最上方加上一个 package 语句指定该代码在哪个包中.
  • 包名需要尽量指定成唯一的名字, 通常会用公司的域名的颠倒形式(例如 com.bit.demo1 ).
  • 包名要和代码路径相匹配. 例如创建 com.bit.demo1 的包, 那么会存在一个对应的路径 com/bit/demo1 来存储代码.
  • 如果一个类没有 package 语句, 则该类被放到一个默认包中.
4.1.2、常见的包
  1. java.lang:系统常用基础类(String、Object),此包从JDK1.1后自动导入。
  2. java.lang.reflect:java 反射编程包;
  3. java.net:进行网络编程开发包。
  4. java.sql:进行数据库开发的支持包。
  5. java.util:是java提供的工具程序包。(集合类等) 非常重要
  6. java.io:I/O编程开发包。

4.2、访问限定修饰符

在java中有四种访问限定修饰符来控制方法和字段是否能在类外使用:

5、static修饰符

在Java中static修饰符的功能与C++很相似,但要注意:在Java中static不能用来修饰局部静态变量全局(文件作用域限制)变量,只能修饰类中的成员变量和成员方法

5.1、static修饰成员变量

  • static修饰的成员变量,称为静态成员变量 ,静态成员变量最大的特性:不属于某个具体的对象,是所有对象所共享的。
  • 既可以通过对象访问,也可以通过类名访问,但一般更推荐使用类名访问
  • 类变量存储在方法区当中

5.2 static修饰成员方法

  • Java中,被static修饰的成员方法称为静态成员方法 ,是类的方法,不是某个对象所特有的。静态成员变量一般是通过静态方法来访问 的。
  • 可以通过对象调用,也可以通过类名.静态方法名(...)方式调用,更推荐使用后者
  • 不能在静态方法中访问任何非静态成员变量和非静态方法,因为在静态方法中没有this引用。
  • 静态方法无法重写,不能用来实现多态

5.3静态成员变量的初始化

静态成员变量一般不会放在构造方法中来初始化 ,构造方法中初始化的是与对象相关的实例属性

静态成员变量的初始化分为两种:就地初始化静态代码块初始化

  1. 就地初始化:在定义时直接给出初始值(显示初始化)
  2. 静态代码初始化:接下来即将讲到代码块这个概念

6、代码块

6.1、代码块概念以及分类

使用 {} 定义的一段代码称为代码块。根据代码块定义的位置以及关键字,又可分为以下四种:

  • 普通代码块
  • 构造块
  • 静态块
  • 同步代码块:涉及多线程,这里不进行介绍

6.2、普通代码块

  • 定义在方法中的代码块
java 复制代码
public class Main{
public static void main(String[] args) {
    { //直接使用{}定义,普通方法块
		int x = 10 ;
   	    System.out.println("x1 = " +x);
    }
    int x = 100 ;
    System.out.println("x2 = " +x);
  }
}

6.3、构造代码块

  • 构造块:定义在类中 的代码块(不加修饰符 )。也叫:实例代码块 。构造代码块一般用于初始化实例成员变量
java 复制代码
/**
 * 构造代码块演示类
 */
public class ConstructorBlockDemo {
    // 实例变量
    private int value;
    
    /* 构造代码块 - 每次创建对象时都会执行 */
    {
        System.out.println("【构造代码块执行】");
        value = 100;  // 初始化实例变量
        printMessage();  // 调用方法
    }
    
    // 无参构造器
    public ConstructorBlockDemo() {
        System.out.println("【无参构造器执行】value = " + value);
    }
    
    // 带参构造器
    public ConstructorBlockDemo(int input) {
        this.value = input;
        System.out.println("【带参构造器执行】value = " + value);
    }
    
    // 成员方法
    private void printMessage() {
        System.out.println("-- 在代码块中调用方法 --");
    }

    public static void main(String[] args) {
        System.out.println("创建第一个对象:");
        new ConstructorBlockDemo();
        
        System.out.println("\n创建第二个对象:");
        new ConstructorBlockDemo(200);
    }
}

6.4、静态代码块

  • 使用static定义的代码块称为静态代码块,一般用于初始化静态成员变量
java 复制代码
public class StaticBlockDemo {
    // 静态变量
    static int staticValue;
    
    // 静态代码块
    static {
        System.out.println("静态代码块执行中...");
        staticValue = 42;  // 初始化静态变量
        System.out.println("已初始化静态变量: staticValue = " + staticValue);
    }
    
    // 实例代码块
    {
        System.out.println("实例代码块执行中...");
    }
    
    // 构造方法
    public StaticBlockDemo() {
        System.out.println("构造方法执行中...");
    }
    
    public static void main(String[] args) {
        System.out.println("--- 第一次创建实例 ---");
        StaticBlockDemo demo1 = new StaticBlockDemo();
        
        System.out.println("\n--- 第二次创建实例 ---");
        StaticBlockDemo demo2 = new StaticBlockDemo();
    }
}
  • 静态代码块不管生成多少个对象,其只会执行一次
  • 静态成员变量是类的属性,因此是在JVM加载类时开辟空间并初始化的
  • 如果一个类中包含多个静态代码块 ,在编译代码时,编译器会按照定义的先后次序依次执行

7、初始化执行顺序

如果一个类中包含所有的初始化方式,那么它们的执行优先级是什么?

初始化方式包括(就地初始化,构造代码块(实例代码块)初始化,静态代码块初始化,构造方法初始化,静态成员就地初始化)

我们接下来区分它们的优先级 (同一梯队的优先级没有先后,看谁定义在前面

  • 第一梯队(最高):(在类加载时进行)静态代码块,静态成员初始化
  • 第二梯队:就地初始化,构造代码块(实例代码块)
  • 第三梯队: 构造方法初始化
java 复制代码
class Example {
   // 静态变量初始化
   static int a = init("静态变量 a");
   // 静态初始化块
   static {
       System.out.println("静态代码块");
   }
   // 实例变量初始化
   int b = init("实例变量 b");
   // 实例初始化块
   {
       System.out.println("实例代码块");
   }
   // 构造方法
   public Example() {
       System.out.println("构造方法");
   }
   // 另一个实例变量
   int c = init("实例变量 c");

   static int init(String s) { System.out.println(s); return 0; }
}

因为静态区域只在类加载时执行一次,所以实例化对象顺序分为两种:

第一次实例化对象顺序:

静态变量 a

静态代码块

实例变量 b

实例代码块

实例变量 c

构造方法

第二次或更多次实例化对象顺序:

实例变量 b

实例代码块

实例变量 c

构造方法

8、内部类

当一个类中的内部还需要一个完整的结果进行描述,那么我们可以在这个类中的内部再定义一个类,这个类就叫内部类

注意:

  • 类名当文件的类不能当内部类
  • 内部类和外部类共用同一个java源文件,但是经过编译之后,内部类会形成单独的字节码文件
  • 在JDK16 + 以后 内部类中才允许定义静态成员

8.1、内部类的分类

java 复制代码
public class OutClass {
    // 成员位置定义:未被static修饰 --->实例内部类
    public class InnerClass1{
   }
 
    // 成员位置定义:被static修饰 ---> 静态内部类
    static class InnerClass2{
 
   }
 
 
    public void method(){
        // 方法中也可以定义内部类 ---> 局部内部类:几乎不用
        class InnerClass5{
 
       }
   }
    public static void main(String[] args) {
        Thread t1 = new Thread(){    //在只执行一次的实例化中也可以定义内部类
        @Override       			//--->匿名内部类,这个以后再细说
            public void run() {
                while (true) {
                    System.out.println();
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                }
            }
        };
    }
}

8.2、实例内部类

接下来我们按照 内部类外部类外部类的其他内部类 之间的关系来介绍访问权限关系

8.2.1、内部类 → 外部类
  • 实例内部类对象必须在先有外部类对象前提下才能创建
  • 实例内部类的非静态方法 中隐式持有外部类对象的引用(外部类名.this),
  • 在实例内部类方法中访问同名的成员时,优先访问自己的,如果要访问外部类同名的成员,必须:外部类名称.this.同名成员 来访问
  • 实例成员:可以直接访问外部类所有实例变量和方法,即使为 private
  • 静态成员:可以直接访问外部类所有静态变量和静态方法,即使为 private
8.2.2、外部类 → 内部类
  • 外部类可以直接访问内部类的所有成员 (包括 private 和静态非静态),但前提是必须先创建内部类的对象
  • 创建内部类对象需通过外部类实例 :outer.new Inner()(在外部类方法中 可简写为 new Inner(),因为 this 就是外部实例
8.2.3、内部类 ↔ 外部类的其他内部类
  • 同一个外部类 中的多个实例内部类之间,可以互相访问对方的私有成员,因为是同一个外部类的成员,享有"家庭成员"权限。

8.3、静态内部类

静态内部类不依赖外部类实例 ,可以独立存在。因此它的权限核心是:只能直接访问外部类的静态成员,不能直接访问外部类的实例成员

8.3.1、内部类 → 外部类
  • 静态内部类的方法中没有隐式的外部类对象引用(没有 Outer.this)
  • 静态成员:可以直接访问外部类的所有静态变量和静态方法,即使为 private。
  • 实例成员: 不能直接访问 外部类的实例变量和方法 。若要访问,必须显式创建外部类对象(new Outer().xxx)。
8.3.2、外部类 → 内部类

外部类 同样可以访问静态内部类的所有成员 (包括 private),但创建方式更自由,因为不需要外部实例。

  • 创建对象:直接 new StaticInner()(在外部类内部可省略类名限定)。
  • 可访问内部类的实例成员(通过对象)和静态成员(直接通过类名)
  • 内部类可以定义任何静态成员(变量、方法、初始化块),没有任何限制。
8.3.3、内部类 ↔ 外部类的其他内部类

同一个外部类中的多个静态内部类(或与实例内部类)之间,可以互相访问私有成员,理由同样是它们都是外部类的成员,享有"类内部"权限。

  • 静态内部类 → 静态内部类:直接创建对象(new OtherStaticInner()),访问其所有成员。
  • 静态内部类 → 实例内部类:先有外部实例(new Outer().new InstanceInner
    ()),然后可访问其所有成员。
  • 实例内部类 → 静态内部类:直接创建对象并访问,没有限制

8.4、局部内部类

定义在外部类的方法体或者{}中,该种内部类只能在其定义的位置使用,一般使用的非常少

  1. 局部内部类只能在所定义的方法体内部使用
  2. 不能被public、static等修饰符修饰
  3. 编译器也有自己独立的字节码文件,命名格式:外部类名字$数字内部类名字.class

9、对象的打印

在java中,对象的打印也是同样直接使用 System.out.println

若要以自定义格式打印对象,应该如何实现?

java中,使用System.out.println打印对象时,会自动调用该对象的toString方法,所以我们只要重写该对象中的toString方法即可

所以两者的关系可以看作:

java 复制代码
Object obj = new Object();
System.out.println(obj);  // 等价于 System.out.println(obj.toString());

在此说明 Object 类是java中所有类的父类,而toString()其实是Object的方法,所以在具体的类中修改Object 其实是属于继承多态中的重写

在没有重写的情况下默认的toString是这样子的:

java 复制代码
public String toString() {
    return getClass().getName() + "@" + Integer.toHexString(hashCode());
}

打印对象会出现哈希码

实际开发中,我们通常会重写 toString 来返回有意义的信息:

java 复制代码
class Person {
    String name;
    int age;
    @Override
    public String toString() {
        return "Person{name='" + name + "', age=" + age + "}";
    }
}

返回的就是return 后面 " "中的内容

相关推荐
时空系1 小时前
第8篇:模板与实例——面向对象编程入门(上)python中文编程
开发语言·python
故事还在继续吗1 小时前
常见的导致 coredump 的原因
开发语言·gdb
Devin~Y1 小时前
大厂Java面试实录:Spring Boot/Cloud + Redis/Kafka + JWT + RAG/Agent(小Y翻车版)
java·spring boot·redis·spring cloud·kafka·spring security·jwt
咸甜适中1 小时前
rust格式化输出(println!、format!、...)
开发语言·rust
CQU_JIAKE1 小时前
【a]4.25
开发语言
张健11564096481 小时前
std::ranges、std::views和懒加载
开发语言·c++
gCode Teacher 格码致知1 小时前
Javascript提高:一个彩色小球在画布边界内反弹并留下渐变轨迹-由Deepseek产生
开发语言·javascript
瞎折腾啥啊2 小时前
现代 CMake 目标系统
c++·cmake·cmakelists
盐焗鹌鹑蛋2 小时前
【C++】list类
c++