文章目录
- 《C++转java快速入手系列》类与对象篇
-
- 1、类与对象
- 2、this引用
- 3、对象的初始化与构造函数
- 4、封装
- 5、static修饰符
-
- 5.1、static修饰成员变量
- [5.2 static修饰成员方法](#5.2 static修饰成员方法)
- 5.3静态成员变量的初始化
- 6、代码块
- 7、初始化执行顺序
- 8、内部类
-
- 8.1、内部类的分类
- 8.2、实例内部类
-
- [8.2.1、内部类 → 外部类](#8.2.1、内部类 → 外部类)
- [8.2.2、外部类 → 内部类](#8.2.2、外部类 → 内部类)
- [8.2.3、内部类 ↔ 外部类的其他内部类](#8.2.3、内部类 ↔ 外部类的其他内部类)
- 8.3、静态内部类
-
- [8.3.1、内部类 → 外部类](#8.3.1、内部类 → 外部类)
- [8.3.2、外部类 → 内部类](#8.3.2、外部类 → 内部类)
- [8.3.3、内部类 ↔ 外部类的其他内部类](#8.3.3、内部类 ↔ 外部类的其他内部类)
- 8.4、局部内部类
- 9、对象的打印
《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最先开始赋予的默认值)
- 字段中 就地初始化
java
public class Demo {
int a = 10; // 显式赋值
String s = "hello"; // 显式赋值
}
- 构造体中
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; // 最终执行此处
}
}
- 实例初始化块(非静态代码块)
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、常见的包
- java.lang:系统常用基础类(String、Object),此包从JDK1.1后自动导入。
- java.lang.reflect:java 反射编程包;
- java.net:进行网络编程开发包。
- java.sql:进行数据库开发的支持包。
- java.util:是java提供的工具程序包。(集合类等) 非常重要
- 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静态成员变量的初始化
静态成员变量一般不会放在构造方法中来初始化 ,构造方法中初始化的是与对象相关的实例属性
静态成员变量的初始化分为两种:就地初始化 和 静态代码块初始化。
- 就地初始化:在定义时直接给出初始值(显示初始化)
- 静态代码初始化:接下来即将讲到代码块这个概念
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、局部内部类
定义在外部类的方法体或者{}中,该种内部类只能在其定义的位置使用,一般使用的非常少
- 局部内部类只能在所定义的方法体内部使用
- 不能被public、static等修饰符修饰
- 编译器也有自己独立的字节码文件,命名格式:外部类名字$数字内部类名字.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 后面 " "中的内容