java数据类型解析以及相关八股文的题 String 到底是基本类型还是引用类型?

Java中的数据类型有哪些?分为哪两大类? (考点:Java数据类型及其分类) 【简单】

还有这个Integer Integer是int 的引用数据类型,

在数据库开发中,绝大多数情况下必须用 Integer 而不是 int,核心原因就三个字:"怕没填" (也就是能处理 null)。

与C++的对比与助记

1. 核心思想:Java 是"强迫症",C++ 是"自由派"

在 C++ 里,数据类型的大小往往看操作系统的脸色(比如 int 在 16 位机和 32 位机上大小不一样)。而且 C++ 有各种 unsigned(无符号),还有满天飞的指针。

但 Java 的设计理念是 "Write Once, Run Anywhere(一次编写,到处运行)"

为了实现这个吹过的牛,Java 变成了一个"极度自律的强迫症":它把所有基本数据类型的大小全部钉死了,不论在什么机器上,永远一样大! 并且,它没收了你直接操作内存的权利(干掉了显式指针)。


2. 用 C++ 助记 Java 的"八大基本类型"

我们把这 8 种类型分成四大家族,对比 C++ 来记:

🎸 第一家族:整数型(整整齐齐,绝无 unsigned
  • Java 只有有符号数 ,没有 C++ 里那种 unsigned int
  • byte (8位) :这是 Java 特有的!在 C++ 里你通常得用 signed char 或者 int8_t 来代替。它专门用来处理底层文件流或网络传输,非常省空间。
  • short (16位) :对应 C++ 的 short
  • int (32位):永远是 32 位!不再像 C++ 那样摇摆不定。
  • long (64位) :在 Java 里不管什么系统,long 就是 64 位。记得赋值时屁股后面要加个 L(如 100L)。
🌊 第二家族:浮点型(几乎一样)
  • float (32位)double (64位) :这俩跟 C++ 几乎一模一样。唯一的考点是:Java 也是个严格的家伙,写 float f = 3.14; 会报错!因为带小数点的字面量默认是 double,大塞小塞不进去,必须写成 3.14f
🎭 第三家族:字符型(格局打开了)
  • C++ 的 char:8位,通常装的是 ASCII 码,只能装英文字母和简单符号。
  • Java 的 char16位! Java 诞生得晚,格局大,直接用了 Unicode 编码。这意味着 Java 的一个 char 可以直接装下一个中文字符!比如 char c = '牛'; 在 Java 里是完全合法的。
🚦 第四家族:布尔型(楚河汉界,极其死板)
  • C++ 的 bool :很随便,0 就是 false,非 0 就是 true。你可以写 if(1)
  • Java 的 boolean :极其严格!它和整数彻底断绝关系 。不能拿 0false,也不能参与任何类型转换。你只能老老实实写 if(true) 或者 if(flag == true)

3. 用 C++ 助记 Java 的"引用数据类型"

你笔记里写了:类、接口、数组。

用 C++ 的话翻译极其简单:Java 的引用数据类型,本质上就是 C++ 里被阉割了运算功能、但极其安全的"指针"!

  • 在 C++ 里,int* p 是个指针,它里面存的是个内存地址。
  • 在 Java 里,你 new 出来一个对象 Student s = new Student();,这个变量 s 就叫"引用"。它里面存的也是对象在堆内存里的地址。你不能对 s 进行 s++ 这种指针偏移运算,Java 会帮你自动管理这块内存(垃圾回收 GC)。

4. 运算时的"潜规则"(自动类型提升)

你笔记里有一句极其关键的话:"byte,short,char 参与运算时,会自动转换为 int 类型。"

费曼解说 :CPU 的寄存器通常是 32 位的。为了算得快,Java 规定,凡是比 int 小的家伙(byte, short, char),只要一做加减乘除,CPU 就会嫌它们太小,直接一把将它们拽大到 int 级别再算 。算完之后,结果也是个 int


🎯 检验时刻:Java 基础防坑测试题

不看上面的笔记,尝试用直觉回答这 4 个问题。答对了,这段八股文你就彻底融会贯通了!

  1. 判断对错并解释 :在 Java 中,因为 if(a) 这种写法更简洁,所以当我们声明了 int a = 1; 时,可以直接写 if(a) { System.out.println("Hello"); }。(提示:想想 Java 的布尔型性格)
  2. 找茬题 :我写了这行代码 float pi = 3.14; 编译器为什么给我标红报错了?我该怎么改?
  3. 计算推演题 :已知 byte b1 = 10; byte b2 = 20;,我接着写 byte b3 = b1 + b2;,这行代码能编译通过吗?为什么?(核心考点:潜规则)
  4. C++老司机翻车题 :在 C++ 里 char c = '中'; 很容易溢出报错,但在 Java 里 char c = '中'; 却完全没问题。请用一句话解释原因。

回答:

1 错

  1. float pi = 3.14f

  2. 能,编译的时候自动给byte 转换成int 类型,隐式转换小转大

  3. java中char 16 位 ,C++中8位

3 回答错误了

因为会隐式转换,所以相加之后变成了int 类型 32位 ,byte 反而放不下了

怎么改才能跑通?

顺从它(大接大):int b3 = b1 + b2; (最推荐)

强转它(我按手印我负责):byte b3 = (byte)(b1 + b2); (告诉编译器:出了事我自己担着,强转!)

对于引用数据类型

类 接口 数组 哪里应用了引用

.

在 C++ 里,int* p 是个指针,它里面存的是个内存地址。

在 Java 里,你 new 出来一个对象 Student s = new Student();,这个变量 s 就叫"引用"。它里面存的也是对象在堆内存里的地址。你不能对 s 进行 s++ 这种指针偏移运算,Java 会帮你自动管理这块内存(垃圾回收 GC)。

类 接口 数组 就是都用到了引用是吧,java ,对于类,就是类 在对象实例化的时候,就是对象其实就是一种引用

数组名就是一个存着内存地址的引用(相当于 C++ 里的数组名退化成的指针)

大方向完全正确!特别是对数组的理解,你极其精准:数组名就是一个存着内存地址的引用(相当于 C++ 里的数组名退化成的指针)

但是,关于「类和对象」的那句话,咱们得稍微纠正一个初学者极容易混淆的概念。你说"对象其实就是一种引用,给类起个新名字换个新地址",这里有点把图纸实体遥控器搞混了。

为了通俗易懂,咱们把这三个词掰开揉碎:类 (Class)对象 (Object)引用 (Reference)


1. 核心比喻:图纸、电视机与遥控器

  • 类 (Class) = 图纸 / 模具 。它只是写在硬盘里的一段代码,规定了长什么样,比如 class Student 就是张图纸,它不占实际运行时的内存。
  • 对象 (Object) = 按照图纸造出来的"电视机"实体 。当你使用 new Student() 时,Java 会在宽敞的堆内存 (Heap) 里,真的划出一大块空间,把这台电视机造出来。
  • 引用 (Reference) = 操控这台电视机的"遥控器" 。当你写 Student s = ... 的时候,这个 s 就是引用。它很小,通常存在栈内存 (Stack) 里,里面只存了一串数字(比如 0x7788),这个数字就是电视机在堆内存里的地址

所以,完整的动作 Student s = new Student(); 其实是分三步走的:

  1. new Student():在堆里造了一台真电视机(对象)。
  2. Student s:在栈里配了一个遥控器(引用变量)。
  3. = (赋值):把电视机的地址,录入到遥控器里。把它们连起来!

2. 用 C++ 翻译一遍,秒懂!

在 C++ 里,这行代码的等价物其实是:
Student* s = new Student();

  • new Student() 同样是在堆(Heap)上分配了内存。
  • Student* s 是一个指针变量(在 Java 里叫引用)。
  • 如果你想调用方法,C++ 是用箭头 s->study(),而在 Java 里统一简化成了点 s.study()

所以,引用不是给类起了个新名字,引用是指向内存中那个实实在在的对象的"安全指针"。


3. 那"接口"是怎么用引用的呢?

接口其实玩的是**"多态"**(万能遥控器)。

比如有个接口叫 Flyable(会飞的),有个类叫 Bird 实现了它。

你可以这么写:
Flyable f = new Bird();

  • new Bird():在堆里造了一只真正的鸟(对象)。
  • Flyable f:你手里拿的是一个只带有"飞行按键"的通用遥控器(接口引用)。
  • 不管内存里是一只鸟还是一架飞机,只要它能飞,你手里的这个 f 遥控器(引用)就能存它的地址,并且按下 f.fly() 按钮让它起飞!
  • C++ 翻译:这就像是父类指针指向子类对象。

总结一下你的思路

  • 类实例化Student s = new Student(); -> s 是引用,存了真 Student 对象的地址。
  • 接口实现Flyable f = new Bird(); -> f 是引用,存了真 Bird 对象的地址。
  • 数组int[] arr = new int[5]; -> arr 是引用,存了那一长溜 5 个格子的首地址。

发现没?只要你看到了 new 关键字,就说明在堆内存里造了真家伙,而等号左边接住它的那个变量,全都是引用(存地址的遥控器)!

"Java 传参到底是值传递,还是引用传递?"

第一:String 到底是基本类型还是引用类型?为什么不可变?

1. 结论抢答

String 百分之百是引用数据类型!

Java 的基本数据类型只有那固定的 8 种(byte, short, int, long, float, double, char, boolean)。String 首字母大写,它是一个正儿八经的类 (Class)

2. 什么是"不可变" (Immutable)?(对比 C++)

在 C++ 里,std::string s = "abc";,你完全可以写 s[0] = 'z';,把它变成 "zbc"。C++ 的字符串是可以随意修改的。

但在 Java 里,String 对象一旦被造出来,就绝对不可能被改变!

如果你写了这样的代码:

java 复制代码
String s = "Hello";
s = s + " World"; // 拼接字符串

表面上看 s 变成了 "Hello World",但底层真相是:

原来那个装有 "Hello" 的内存块根本没变 !Java 只是在堆内存里新建 了一个叫 "Hello World" 的新对象,然后把 s 这个遥控器,重新指向了新对象的地址。原来的 "Hello" 像个孤儿一样被留在了内存里(等待垃圾回收)。

3. 为什么它能做到不可变?(底层的秘密)

如果你翻开 Java String 类的源码,你会发现它极其"鸡贼":

  1. 底层的阵列被锁死了String 内部其实是用一个字符数组 char[] (JDK 9 之后变成了 byte[]) 来存数据的。但这个数组被声明为 private finalprivate 代表你拿不到它,final 代表它一旦赋值地址就不能换。
  2. 不给任何修改方法String 类没有提供任何类似 setChar(index, 'a') 的方法。
  3. 类被绝育了String 类被 final 修饰,意味着你不能写个子类去继承它、篡改它的行为。它就像一个全封闭的防弹玻璃盒
4. 面试官追问:为什么要设计成不可变?
  • 安全:数据库密码、网络地址经常用 String 传。如果能被悄悄修改,黑客就乐疯了。
  • 线程安全:因为谁也改不了它,所以多线程随便怎么读都不用加锁,绝对安全。
  • 终极原因:为了省内存(字符串常量池) 。因为不可变,Java 就可以在内存里建一个"缓存池"。如果你写 String a = "abc"; String b = "abc";,Java 不会造两个对象,而是让 ab 这两个遥控器指向同一个 "abc"。只有不可变,才敢这么共享!

第二局:世纪大坑------Java 到底是值传递,还是引用传递?

请死死记住这个标准答案:Java 只有值传递(Pass-by-Value),绝对没有引用传递!

很多有 C++ 基础的人会在这里拍桌子:"不对啊!我把一个对象传进函数里,修改了它的属性,外面的对象也变了啊,这难道不是引用传递?"

别急,我们上**"遥控器"**比喻!

1. 基本类型的传递:复印纸条
java 复制代码
void change(int x) { x = 10; }
int a = 5;
change(a); // a 还是 5

这个好理解,a 是一张写着 5 的纸条。传参的时候,Java 复印 了一张纸条递进函数里。你在函数里把复印件改成 10,关外面的原件什么事?这叫值传递

2. 对象的传递:配了一把新遥控器
java 复制代码
class TV { String channel = "CCTV-1"; }

void changeTV(TV remote2) {
    remote2.channel = "CCTV-5"; // 动作 A
    remote2 = new TV();         // 动作 B
}

TV remote1 = new TV();
changeTV(remote1);

高能预警,开始推演:

  1. 外面造了一台真电视,remote1(遥控器1号)存了它的地址(比如 0x111)。
  2. 调用函数 changeTV(remote1)。注意!Java 这里做的依然是值传递 !传递的是什么"值"?传递的是遥控器里的那串地址数字!
  3. 也就是说,Java 在函数内部,克隆了一把一模一样的新遥控器 remote2 ,里面也存着 0x111
  4. 动作 A :你用 remote2 按下了换台键(remote2.channel = "CCTV-5")。因为两个遥控器对准的是同一台电视,所以外面的电视确实换台了!(这就是让人误以为是引用传递的幻觉来源)。
  5. 动作 B(见证奇迹的时刻) :你在函数里写 remote2 = new TV()。这意味着,你在函数里造了一台新电视(地址 0x222),并把函数里的那把新遥控器 remote2 对准了新电视。
  6. 结果 :外面的 remote1 依然死死地指向原来的旧电视(0x111)。它根本没有被改变!
3. C++ 老司机的顿悟时刻

如果你用 C++ 翻译 Java 的对象传参,它完全等于 C++ 里的传指针(传值) ,而不是传引用(&)

cpp 复制代码
// Java 的行为完全等价于下面这段 C++ 代码:
void changeTV(TV* remote2) {
    remote2->channel = "CCTV-5"; // 修改了同一个对象的内容
    remote2 = new TV();          // 仅仅是改变了局部指针的指向,外部指针 remote1 纹丝不动!
}

如果在 C++ 里想做真正的"引用传递",你得写成 void changeTV(TV*& remote2)(传指针的引用),这样你修改 remote2 的指向,外面的指针才会跟着变。但 Java 底层根本没给你这种能力!


🏆 费曼总结检验

  1. String 是个只能看不能改的"防弹玻璃盒"(引用类型)。每次你以为修改了它,其实都是系统偷偷给你换了个新的。
  2. Java 传参 ,永远都是在"复印"。传基本类型就复印数字,传对象就复印遥控器(地址) 。因为你在函数里砸烂克隆遥控器,影响不到外面的原版遥控器,所以 Java 绝对是值传递
相关推荐
蜡笔削薪几秒前
财联万业(杭州)数字科技有限公司能否给代理划定独家经营区域?
大数据·人工智能·python·科技
豆瓣鸡7 分钟前
封装 API 请求日志切面——从注解定义到 Starter 自动装配
java·开发语言·spring boot
站大爷IP13 分钟前
Python的字典合并坑了我三天,原来是踩了这个坑
python
Tbisnic29 分钟前
从链式法则到ReLU:梯度消失的数学本质与工程解决方案
python·深度学习·大模型·激活函数·梯度消失
掉鱼的猫42 分钟前
把 OpenAPI 接入 Agent Harness:零代码让 Agent 听懂你的 REST API
java·llm·agent
lzhcoder1 小时前
Spring Boot 集成 Kafka:先跑通 Demo,再避开那 80% 的人踩过的坑
java·kafka
想会飞的蒲公英1 小时前
一个 PyTorch 模型训练的完整流程
人工智能·pytorch·python
c_lb72881 小时前
最新AI量化练习,小策略更适合练流程感
人工智能·python
plainGeekDev1 小时前
ProGuard → R8
android·java·kotlin
带刺的坐椅1 小时前
把 OpenAPI 接入 Agent Harness:零代码让 Agent 听懂你的 REST API
java·ai·llm·agent·solon·restapi·openapi