0%

本章是整理知识内容,为强化知识长期更新。

网络协议

网络协议是一组确定的规则,这些规则确定如何在同一网络中的不同设备之间传输数据。本质上,它允许连接的设备彼此通信,而不管其内部过程,结构或设计是否有差异。(两个端点都需要了解协议才能进行交流)。

OSI网络模型

OSI英文全称叫做(Open System Interconnection Model). 中文全称叫做开放式系统互联模型. 也叫做网络7层模型,从下到上依次为,物理层,数据链路层,网络层,传输层,会话层,表示层,应用层。

  • OIS概念模型实际上并没有真正实现过,但是我们需要参考这个分层来理解网络协议一般来说把5-7层叫做上层,1-4层叫做下层。

  • 7-应用层 -> 网络流程应用(表示的是用户界面,例如Telnet,HTTP、FTP、TFTP、SMTP、SNMP、DNS、TELNET、HTTPS、POP3、DHCP)
  • 6-表示层 -> 数据表示 (数据如何呈现,特殊处理->例如加密,比如JPEG、ASCLL、EBCDIC、加密格式等)
  • 5-会话层 -> 主机间的通信(将不同应用程序的数据分开。建立,管理和终止应用之间的会话)
  • 4-传输层 -> 端到端连接(可靠或不可靠的传递,例如TCP,UDP)
  • 3-网络层 -> 地址和最佳路径(提供路由器用于路径的逻辑寻址,比如ICMP IGMP IP(IPV4 IPV6))
  • 2-数据链路层 -> 媒体访问(将位组合成字节,将字节组合成帧,使用MAC地址访问,错误检测-比如HDLC)
  • 1-物理层 -> 二进制传输(在设备之间移动bits。例如V.35)

TCP/IP网络模型

TCP/IP 模型和OSI相比会简单一点,只有四层,分别为数据链路层,网络层,传输层和应用层。

OSI模型与TCP/IP模型

  • 4-应用层 -> 对应于OSI的5-7层
  • 3-传输层 -> 这个是和OSI的第四层想同的
  • 2-网络层 -> 这个是和OSI的第3网络层对应的
  • 1-网络访问层 -> 这个是和OSI的第1-2层所对应的
Read more »

学习下Rust

为什么学习Rust

在知乎上看到一个人对Rust的评论。

首先,Rust 是有点反人类,否则不会一直都不火。然后,Rust 之所以反人类,是因为人类这玩意既愚蠢,又自大,破事还贼多。 你看 C++ 就很相信人类,它要求人类自己把自己 new 出来的东西给 delete 掉。 C++:“这点小事我相信你可以的!” 人类:“没问题!包在我身上!” 然后呢,内存泄漏、double free、野指针满世界飘…… C++:“……”

Java 选择不相信人类,但替人类把事办好。 Java:“别动,让我来,我有gc!” 人类:“你怎么做事这么慢呀?你怎么还 stop the world 了呀?你是不是不爱我了呀?” Java:“……”

Rust 发现唯一的办法就是既不相信人类,也不惯着人类。 Rust:“按老子说的做,不做就不编译!” 人类:“你反人类!” Rust:“滚!”

  • C/C++ 完全相信而且惯着程序员,让大家自行管理内存,所以可以编写很自由的代码,但一个不小心就会造成内存泄漏等问题导致程序崩溃。
  • Java/Golang 完全不相信程序员,但也惯着程序员。所有的内存生命周期都由 JVM 运行时统一管理。 在绝大部分场景下,你可以非常自由的写代码,而且不用关心内存到底是什么情况。 内存使用有问题的时候,我们可以通过 JVM 来信息相关的分析诊断和调整。
  • Rust 语言选择既不相信程序员,也不惯着程序员。 让你在写代码的时候,必须清楚明白的用 Rust 的规则管理好你的变量,好让机器能明白高效地分析和管理内存。 但是这样会导致代码不利于人的理解,写代码很不自由,学习成本也很高。

安装Rust开发环境

安装Rust

Rust在Mac系统上非常的友好

1
curl https://sh.rustup.rs -sSf | sh
  • 脚本会检测你的操作系统,然后提示你是否安装

  • 默认选择1就好了

  • 这样就安装好了
1
2
3
4
source $HOME/.cargo/env
# 设置下Rust的环境变量
rustc --version
# 如果正常显示版本说明安装成功了

创建测试项目

1
2
3
4
5
6
7
8
mkdir learning
cd learning
cargo new learning-01
cd learning-01
cargo run

Hello, world!
# 如果变成成功并输出消息则表示测试项目通过了。

Rust

Rust 是一个系统级编程语言,被 Mozilla 员工 “Graydon Hoare” 于 2006 年 开发。他形容 Rust 是一种线程安全的支持并发的实用型的编程语言,支持函数式编程与命令式编程。

Cargo

Cargo是Rust的构建系统和包管理工具,和Java中maven类似的功能。

1
2
// 查看版本
cargo --version
  • 创建一个项目
1
2
3
4
5
6
7
8
9
cargo new new_project
cd new _project
➜ new_project git:(master) ✗ tree
.
├── Cargo.toml
└── src
└── main.rs

1 directory, 2 files
  • 这里的Cargo.toml就是配置文件 TOML (Tom’s Obvious, Minimal Language) 格式,这是 Cargo 配置文件的格式。
1
2
3
4
5
6
7
8
[package]
name = "new_project"
version = "0.1.0"
edition = "2021"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
  • 构建并运行
1
2
3
4
5
6
7
8
cargo build
➜ new_project git:(master) ✗ cargo build
Compiling new_project v0.1.0 (/Users/z201/word/rust/learning-rust/new_project)
Building [ ] 0/1: new_project(bin)
Finished dev [unoptimized + debuginfo] target(s) in 11.12s

➜ new_project git:(master) ✗ ./target/debug/new_project
Hello, world!
  • 也可以直接编译加运行
1
cargo run
  • 快速检查是否可以编译
1
cargo check

本章是整理知识内容,为强化知识长期更新。

练手项目

牛客网

准备一个mysql 5.7的数据库

配置本地数据库

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
➜  employees_db tree 
.
├── Changelog
├── README
├── employees.sql
├── employees_partitioned.sql
├── employees_partitioned2.sql
├── employees_partitioned3.sql
├── load_departments.dump
├── load_dept_emp.dump
├── load_dept_manager.dump
├── load_employees.dump
├── load_salaries.dump
├── load_titles.dump
├── objects.sql
├── test_employees_md5.sql
└── test_employees_sha.sql
  • 在将文件导入数据库
1
mysql -uroot -p -t < employees.sql

解决问题

  • employees.sql 修改内容
1
2
3
4
# 38行 set storage_engine = InnoDB; 替换
set default_storage_engine = InnoDB;
# 44行 select CONCAT('storage engine: ', @@storage_engine) as INFO; 替换
select CONCAT('storage engine: ', @@default_storage_engine) as INFO;
  • 注意导入的时候会出现文件打不开的问题
1
2
3
4
5
6
7
8
9
10
11
12
SELECT 'LOADING departments' as 'INFO';
source /home/employees_db/load_departments.dump ;
SELECT 'LOADING employees' as 'INFO';
source /home/employees_db/load_employees.dump ;
SELECT 'LOADING dept_emp' as 'INFO';
source /home/employees_db/load_dept_emp.dump ;
SELECT 'LOADING dept_manager' as 'INFO';
source /home/employees_db/load_dept_manager.dump ;
SELECT 'LOADING titles' as 'INFO';
source /home/employees_db/load_titles.dump ;
SELECT 'LOADING salaries' as 'INFO';
source /home/employees_db/load_salaries.dump ;
  • 这里要将文件正确的路径写进去。

  • 如果是本地的docker环境部署mysql,可以使用docker cp 命令将文件复制到容器中。在登陆到容器执行导入脚本

1
2
3
4
5
6
// 从宿主机器拷贝到容器中
docker cp ~/Downloads/employees_db laughing_black:/home
// laughing_black 是容器的名称

// 从容器中拷贝会宿主机器中
docker cp laughing_black:/employees_db.sql .

本章是整理知识内容,为强化知识长期更新。

JVM

JVM 全称 Java Virtual Machine,也就是我们耳熟能详的 Java 虚拟机。JVM 会翻译执行 Java 字节码,然后调用真正的操作系统函数,这些操作系统函数是与平台息息相关的。

  • 如上图所示,通过JVM,Java实现了跨平台、只要class文件能正常执行,就可以在其他系统上面运行。 JVM 与操作系统之间的关系:JVM 上承开发语言,下接操作系统,它的中间接口就是字节码。

JDK

  • JDK 的全拼,Java Development Kit JVM、JRE、JDK 它们三者之间的关系,可以用一个包含关系表示。

JVM字节码

对于 Java 开发者来说,虚拟机、字节码就是其底层知识。Java Byte 由单字节 bute的指令组成,理论上最多支持256个操作码。实际上只使用了200个左右操作码。

编译java文件

这里采用xxd xx.java

1
2
3
4
5
6
7
8
9

package cn.z201.jvm;

public class Testing {

public static void main(String[] args){
System.out.println("Hello world!");
}
}
  • 通过javac编译成class文件、在用xxd查看编译后的16进制class文件。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
➜  jvm git:(master) ✗ javac Testing.java 
➜ jvm git:(master) ✗ xxd Testing.class
00000000: cafe babe 0000 0034 001d 0a00 0600 0f09 .......4........
00000010: 0010 0011 0800 120a 0013 0014 0700 1507 ................
00000020: 0016 0100 063c 696e 6974 3e01 0003 2829 .....<init>...()
00000030: 5601 0004 436f 6465 0100 0f4c 696e 654e V...Code...LineN
00000040: 756d 6265 7254 6162 6c65 0100 046d 6169 umberTable...mai
00000050: 6e01 0016 285b 4c6a 6176 612f 6c61 6e67 n...([Ljava/lang
00000060: 2f53 7472 696e 673b 2956 0100 0a53 6f75 /String;)V...Sou
00000070: 7263 6546 696c 6501 000c 5465 7374 696e rceFile...Testin
00000080: 672e 6a61 7661 0c00 0700 0807 0017 0c00 g.java..........
00000090: 1800 1901 000c 4865 6c6c 6f20 776f 726c ......Hello worl
000000a0: 6421 0700 1a0c 001b 001c 0100 1363 6e2f d!...........cn/
000000b0: 7a32 3031 2f6a 766d 2f54 6573 7469 6e67 z201/jvm/Testing
000000c0: 0100 106a 6176 612f 6c61 6e67 2f4f 626a ...java/lang/Obj
000000d0: 6563 7401 0010 6a61 7661 2f6c 616e 672f ect...java/lang/
000000e0: 5379 7374 656d 0100 036f 7574 0100 154c System...out...L
000000f0: 6a61 7661 2f69 6f2f 5072 696e 7453 7472 java/io/PrintStr
00000100: 6561 6d3b 0100 136a 6176 612f 696f 2f50 eam;...java/io/P
00000110: 7269 6e74 5374 7265 616d 0100 0770 7269 rintStream...pri
00000120: 6e74 6c6e 0100 1528 4c6a 6176 612f 6c61 ntln...(Ljava/la
00000130: 6e67 2f53 7472 696e 673b 2956 0021 0005 ng/String;)V.!..
00000140: 0006 0000 0000 0002 0001 0007 0008 0001 ................
00000150: 0009 0000 001d 0001 0001 0000 0005 2ab7 ..............*.
00000160: 0001 b100 0000 0100 0a00 0000 0600 0100 ................
00000170: 0000 0500 0900 0b00 0c00 0100 0900 0000 ................
00000180: 2500 0200 0100 0000 09b2 0002 1203 b600 %...............
00000190: 04b1 0000 0001 000a 0000 000a 0002 0000 ................
000001a0: 0008 0008 0009 0001 000d 0000 0002 000e ................
  • 编译后完全看不懂,jdk实际上提供了专门用来解析类文件的工具javap

javap

javap能对给定的class文件提供的字节代码进行反编译

  • 使用格式
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
javap <options> <classes>

-help --help -? 输出此用法消息
-version 版本信息
-v -verbose 输出附加信息
-l 输出行号和本地变量表
-public 仅显示公共类和成员
-protected 显示受保护的/公共类和成员
-package 显示程序包/受保护的/公共类
和成员 (默认)
-p -private 显示所有类和成员
-c 对代码进行反汇编
-s 输出内部类型签名
-sysinfo 显示正在处理的类的
系统信息 (路径, 大小, 日期, MD5 散列)
-constants 显示最终常量
-classpath <path> 指定查找用户类文件的位置
-cp <path> 指定查找用户类文件的位置
-bootclasspath <path> 覆盖引导类文件的位置
  • 演示下上面的类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
javap -c -s -v Testing

Classfile /Users/z201/word/z201.github.io.code/java-learning/learn-jvm/src/test/java/cn/z201/jvm/Testing.class
Last modified 2020-03-01; size 432 bytes
MD5 checksum b50deb6f6e7df9bb99585bc71b92d39b
Compiled from "Testing.java"
public class cn.z201.jvm.Testing
minor version: 0
major version: 52
flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
#1 = Methodref #6.#15 // java/lang/Object."<init>":()V
#2 = Fieldref #16.#17 // java/lang/System.out:Ljava/io/PrintStream;
#3 = String #18 // Hello world!
#4 = Methodref #19.#20 // java/io/PrintStream.println:(Ljava/lang/String;)V
#5 = Class #21 // cn/z201/jvm/Testing
#6 = Class #22 // java/lang/Object
#7 = Utf8 <init>
#8 = Utf8 ()V
#9 = Utf8 Code
#10 = Utf8 LineNumberTable
#11 = Utf8 main
#12 = Utf8 ([Ljava/lang/String;)V
#13 = Utf8 SourceFile
#14 = Utf8 Testing.java
#15 = NameAndType #7:#8 // "<init>":()V
#16 = Class #23 // java/lang/System
#17 = NameAndType #24:#25 // out:Ljava/io/PrintStream;
#18 = Utf8 Hello world!
#19 = Class #26 // java/io/PrintStream
#20 = NameAndType #27:#28 // println:(Ljava/lang/String;)V
#21 = Utf8 cn/z201/jvm/Testing
#22 = Utf8 java/lang/Object
#23 = Utf8 java/lang/System
#24 = Utf8 out
#25 = Utf8 Ljava/io/PrintStream;
#26 = Utf8 java/io/PrintStream
#27 = Utf8 println
#28 = Utf8 (Ljava/lang/String;)V
{
public cn.z201.jvm.Testing();
descriptor: ()V
flags: ACC_PUBLIC
Code:
stack=1, locals=1, args_size=1
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
LineNumberTable:
line 5: 0

public static void main(java.lang.String[]);
descriptor: ([Ljava/lang/String;)V
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=2, locals=1, args_size=1
0: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
3: ldc #3 // String Hello world!
5: invokevirtual #4 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
8: return
LineNumberTable:
line 8: 0
line 9: 8
}
SourceFile: "Testing.java"

IDEA bytecode viewer

jclasslib is a bytecode viewer for Java class files

  • 在plugins中搜索jclasslib bytecode viewer
  • 安装重启IDEA后,编译项目代码(run 一次)选中代码文件 view 菜单中有 bytecode viewer 选项。

  • IDEA 显示的内容更加容易阅读。

JVM的内存布局

  • 堆(Java Heap) 也叫 Java 堆或者是 GC 堆,它是一个线程共享的内存区域,也是 JVM 中占用内存最大的一块区域,Java 中所有的对象都存储在这里。

    • 存储的是我们new来的对象,不存放基本类型和对象引用。

    • 由于创建了大量的对象,垃圾回收器主要工作在这块区域。

    • 线程共享区域,因此是线程不安全的。

    • 能够发生内存溢出,主要有OutOfMemoryError和StackOverflowError。

    • 那么什么时候发生OutOfMemoryError,什么时候发生StackOverflowError?虚拟机在扩展栈时无法申请到足够的内存空间,将抛出OutOfMemoryError异常,线程请求的栈深度超过虚拟机所允许的最大深度,将抛出StackOverflowError异常

    • Java堆区还可以划分为新生代和老年代,新生代又可以进一步划分为Eden区、Survivor 1区、Survivor 2区。具体比例参数的话,可以看一下下面这张图。

    • 合理设置老年代和新生代的空间比例对 JVM 垃圾回收的性能有很大影响,JVM 设置老年代新生代比例的参数是 -XX:NewRatio。

新生代

  • 新生代是类的诞生、成长、消亡的区域,一个类在这里产生、应用,最后被垃圾回收器收集,结束生命。新生区又分为两部分:伊甸区(Eden space)和幸存区(Survivor pace),绝大多数的类都是在伊甸区被 new 出来的。幸存区有两个:0区(from Survivor 0 space)和 1区(to Survivor 1 space)。两个Survivor 区空间一样大,当Eden 区满了,则会触发普通GC,若 Eden 区中的对象经过垃圾回收没有被回收掉,会将Eden 区中的幸存对象移动到幸存0区。若幸存0区也满了,再对该区域进行垃圾回收,然后移动到1区,然后之前的0区将置为空,没有任何数据。显然,Survivor 区只是增加了对象在年轻代中逗留的时间,增加了被垃圾回收的可能性。那如果1区也满了呢?再移动到养老区。若养老区也满了,那么这时候将产生Full GC,进行养老区的内存清理。若养老区执行Full GC 之后发现依然无法进行对象的保存,就会产生 OOM 异常“OutOfMemoryError”。
  • 每次垃圾回收时都有大量的对象需要被回收。垃圾回收器在新生代采用的收集算法是Copying(复制)方法:它将可用内存按容量划分为大小相等的两块,每次只使用其中的一块。当这一块面对内存用完了,就将还存活的对象复制到另外一块,然后再把已使用的内存空间一次清理掉。由于新生代有每次垃圾回收时都有大量的对象需要被回收的特点,所以采用Copying算法,复制的存活对象较少,性能比较好。但是Copying算法还是有缺陷的,那就是内存的使用率只有一半。

OutOfMemoryError

1
java.lang.OutOfMemoryError: Java heap space
  1. Java 虚拟机的堆内存设置不够,可以通过-Xms、-Xmx来调整
  2. 代码中创建了大量大对象,并且长时间不能被垃圾回收器收集
  3. 内存加载的数据量太大:一次性从数据库取太多数据
  4. 集合类中有对对象的引用,使用后未清空,GC不能进行回收
  5. 代码中存在循环产生过多的重复对象

老年代

  • 年轻代中经过垃圾回收没有回收掉的对象将被Copy到年老代。
  • 每次垃圾回收时只有少量的对象需要被回收。垃圾回收器在老年代采用的收集算法是 Mark-Compac t算法:为了解决Copying算法的缺陷,充分利用内存空间,提出了Mark-Compact算法。它先将需要回收的对象进行标记,然后将存活对象都向一端移动,然后清理掉端边界以外的内存。

FULL GC

  • 当老年代空间满了,就会对新生代和老年代空间进行一次全量垃圾回收,即Full GC。

方法区

  • 方法区(Method Area) 也被称为非堆区,用于和“Java 堆”的概念进行区分,它也是线程共享的内存区域,用于存储已经被 JVM 加载的类型信息、常量、静态变量、代码缓存等数据。

运行时常量池

  • Run-Time Constant Pool这是方法区的一部分,版本号、字段、方法、超类、接口等各种信息,还有一项信息就是常量池。Java 的常量池可以存放各种常量信息,不管是编译期生成的各种字面量,还是需要在运行时决定的符号引用。

程序计数器

  • 程序计数器(Program Counter Register) 线程独有一块很小的内存区域,保存当前线程所执行字节码的位置,包括正在执行的指令、跳转、分支、循环、异常处理等。

虚拟机栈

  • 虚拟机栈也叫 Java 虚拟机栈(Java Virtual Machine Stack),和程序计数器相同它也是线程独享的,用来描述 Java 方法的执行,在每个方法被执行时就会同步创建一个栈帧,用来存储局部变量表、操作栈、动态链接、方法出口等信息。当调用方法时执行入栈,而方法返回时执行出栈。

    • 线程私有区域,每一个线程都有独享一个虚拟机栈,因此这是线程安全的区域。

    • 存放基本数据类型以及对象的引用。

    • 每一个方法执行的时候会在虚拟机栈中创建一个相应栈帧,方法执行完毕后该栈帧就会被销毁。方法栈帧是以先进后出的方式虚拟机栈的。

    • 每一个栈帧又可以划分为局部变量表、操作数栈、动态链接、方法出口以及额外的附加信息。

      • 这个区域可能有两种异常:如果线程请求的栈深度大于虚拟机所允许的深度,将抛出StackOverflowError异常(通常是递归导致的);JVM动态扩展时无法申请到足够内存则抛出OutOfMemoryError异常。

本地方法栈

  • 本地方法栈(Native Method Stacks)与虚拟机栈类似,它是线程独享的,并且作用也和虚拟机栈类似。只不过虚拟机栈是为虚拟机中执行的 Java 方法服务的,而本地方法栈则是为虚拟机使用到的本地(Native)方法服务。

  • JVM 的执行流程是,首先先把 Java 代码(.java)转化成字节码(.class),然后通过类加载器将字节码加载到内存中,所谓的内存也就是我们上面介绍的运行时数据区,但字节码并不是可以直接交给操作系统执行的机器码,而是一套 JVM 的指令集。这个时候需要使用特定的命令解析器也就是我们俗称的执行引擎(Execution Engine)将字节码翻译成可以被底层操作系统执行的指令再去执行,这样就实现了整个 Java 程序的运行,这也是 JVM 的整体执行流程。

类的加载机制

符号引用和直接引用有一个重要的区别:使用符号引用时被引用的目标不一定已经加载到内存中;而使用直接引用时,引用的目标必定已经存在虚拟机的内存中了。

  • 类的生命周期会经历以下 7 个阶段:

  1. 加载阶段(Loading)
  2. 验证阶段(Verification)
  3. 准备阶段(Preparation)
  4. 解析阶段(Resolution)
  5. 初始化阶段(Initialization)
  6. 使用阶段(Using)
  7. 卸载阶段(Unloading)

加载阶段

此阶段用于查到相应的类(通过类名进行查找)并将此类的字节流转换为方法区运行时的数据结构,然后再在内存中生成一个能代表此类的 java.lang.Class 对象,作为其他数据访问的入口。

  • Java语言的类型可以分为两大类:基本类型、引用类型。基本类型是由虚拟机预先定义好的,所以不会经历单独的类加载过程。而引用类型又分为四种:类、接口、数组类、泛型参数。由于泛型参数会在编译的过程中被擦除(关于类型擦除的知识,大家可以查下资料),所以在Java中只有类、接口、数组类三种类型需要经历JVM对其进行连接和初始化的过程。

验证阶段

此步骤主要是为了验证字节码的安全性,如果不做安全校验的话可能会载入非安全或有错误的字节码,从而导致系统崩溃,它是 JVM 自我保护的一项重要举措。

  • 验证的主要动作大概有以下几个:
    • 文件格式校验包括常量池中的常量类型、Class 文件的各个部分是否被删除或被追加了其他信息等;
    • 元数据校验包括父类正确性校验(检查父类是否有被 final 修饰)、抽象类校验等;
    • 字节码校验,此步骤最为关键和复杂,主要用于校验程序中的语义是否合法且符合逻辑;
    • 符号引用校验,对类自身以外比如常量池中的各种符号引用的信息进行匹配性校验。

准备阶段

此阶段是用来初始化并为类中定义的静态变量分配内存的,这些静态变量会被分配到方法区上。这些变量所使用的内存都将在方法区(<Jdk1.8)元数据区(>=Jdk1.8)中进行分配。这时候进行内存分配的仅包括类变量,而不包括实例变量,实例变量将会在对象实例化的时候随对象一起分配在Java堆中。

解析阶段

此阶段主要是用来解析类、接口、字段及方法的,解析时会把符号引用替换成直接引用。

  • 所谓的符号引用是指以一组符号来描述所引用的目标,符号可以是任何形式的字面量,只要使用时能无歧义地定位到目标即可;而直接引用是可以直接指向目标的指针、相对偏移量或者是一个能间接定位到目标的句柄。

  • 符号引用和直接引用有一个重要的区别:使用符号引用时被引用的目标不一定已经加载到内存中;而使用直接引用时,引用的目标必定已经存在虚拟机的内存中了。

初始化

初始化阶段 JVM 就正式开始执行类中编写的 Java 业务代码了。到这一步骤之后,类的加载过程就算正式完成了。

  • JVM 规范枚举了下述多种触发情况:
    • 当虚拟机启动时,初始化用户指定的主类;
      • 当遇到用以新建目标类实例的 new 指令时,初始化 new 指令的目标类;
      • 当遇到调用静态方法的指令时,初始化该静态方法所在的类;
      • 当遇到访问静态字段的指令时,初始化该静态字段所在的类;子类的初始化会触发父类的初始化;
      • 如果一个接口定义了 default 方法,那么直接实现或者间接实现该接口的类的初始化,会触发该接口的初始化;
      • 使用反射 API 对某个类进行反射调用时,初始化这个类;
      • 当初次调用 MethodHandle 实例时,初始化该 MethodHandle 指向的方法所在的类。
Read more »

jvm调优是日常工作中经常会使用的技巧,整理下。

项目启动的时候加上的参数都是些啥意思?

刚开始工作的时候发现同事会在jar启动的命令上增加很多参数,很长一段时间都不清楚是干啥的。

1
-XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=128m -Xms1024m -Xmx1024m -Xmn256m -Xss256k -XX:SurvivorRatio=8 -XX:+UseConcMarkSweepGC
  • 简单介绍下参数
1
2
3
4
5
6
7
8
9
-XX:MetaspaceSize=128m (元空间默认大小)
-XX:MaxMetaspaceSize=128m (元空间最大大小)
-Xms1024m (堆最大大小)
-Xmx1024m (堆默认大小)
-Xmn256m (新生代大小)
-Xss256k (棧最大深度大小)
-XX:SurvivorRatio=8 (新生代分区比例 8:2)
-XX:+UseConcMarkSweepGC (指定使用的垃圾收集器,这里使用CMS收集器)
-XX:+PrintGCDetails (打印详细的GC日志)
  • 虽然有了介绍但是依然不清楚具体是干啥的。并且Java虚拟机提供了非常多的参数命令。下面代码可以输出支持的参数数量
1
2
3
4
5
java -XX:+PrintFlagsFinal -XX:+UnlockDiagnosticVMOptions -version | wc -l
openjdk version "1.8.0_275"
OpenJDK Runtime Environment (AdoptOpenJDK)(build 1.8.0_275-b01)
OpenJDK 64-Bit Server VM (AdoptOpenJDK)(build 25.275-b01, mixed mode)
838
  • 堆内存大小配置

建议 -Xms = 最大内存 * [0.6. ~0.8] 这里需要考虑系统损耗内存、和实际物理内存。

  • 堆内存与堆外内存

    • 堆内存

    必须是1024的倍数,且不能低于2M。

    32位机器,最大1G/4G 64位机器最大可以超过 32G/64G

    • 堆外内存

    堆外内存一般指 Direct Memory ,不受GC控制,JVM、Netty都可能使用堆外内存。

1
-XX:MaxDirectMemorySize 限制

如何理解这些参数的含义?

首先我们需要理解java是如何运行的,为什么需要java虚拟机?

我们常用方式一般是安装java运行环境(jre)用命令行的方式启动或者直接双击jar运行。jre包含的java运行的必要环境。

Java 作为一门高级程序语言,它的语法非常复杂,抽象程度也很高。编译出来的也不是机器可以直接直接运行代码。所以使用面向Java语言的虚拟机运行Java编译以后的特定代码。这里的特定代码指的是Java字节指令码。

JVM 内存分配性能问题

  • 在应用服务的特定场景下,JVM 内存分配不合理带来的性能表现并不会像内存溢出问题这么突出。如果没有深入到各项性能指标中去,是很难发现其中隐藏的性能损耗。
  • JVM 内存分配不合理最直接的表现就是频繁的 GC,这会导致上下文切换等性能问题,从而降低系统的吞吐量、增加系统的响应时间。

分析 GC 日志

  • 在进行压测的时候,我们需要对GC日志进行分析。
1
-XX:+PrintGCTimeStamps -XX:+PrintGCDetails -Xloggc:/log/heap.log
  • -XX:PrintGCTimeStamps:打印 GC 具体时间;
  • -XX:PrintGCDetails :打印出 GC 详细日志;
  • -Xloggc: path:GC 日志生成路径。
  • JVM 内存调优通常和 GC 调优是互补的,基于以上调优,可以对年轻代和堆内存的垃圾回收算法进行调优。
Read more »

Netty是一款用于快速开发高性能的网络应用程序的Java框架,正是因为有 Netty 的存在,网络编程领域 Java 才得以与 C++ 并肩而立。

  • Netty 官网给出了有关 Netty 的整体功能模块结构

  • Core核心层

提供底层网络通信抽象和实现,其中包括了可扩展的时间模型、通信API、支持零拷贝的buf等。

  • Protocol Support 协议支持层

协议支持层基本上覆盖了主流协议的编解码实现,比如HTTP、SSL、WebSocket

  • Transport Service 传输服务层

传输服务层提供了网络传输能力的定义和实现方法,支持 Socket、HTTP 隧道等。Netty 对 TCP、UDP 等数据传输做了抽象和封装

Netty是如何启动的

  • 初始化线程池
  • 初始化channel
  • 绑定端口并启动
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78

@Sharable
public class EchoServerHandler extends ChannelInboundHandlerAdapter {

@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) {
ctx.write(msg);
}

@Override
public void channelReadComplete(ChannelHandlerContext ctx) {
ctx.flush();
}

@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
// Close the connection when an exception is raised.
cause.printStackTrace();
ctx.close();
}
}


/**
* Echoes back any received data from a client.
*/
public final class EchoServer {

static final boolean SSL = System.getProperty("ssl") != null;
static final int PORT = Integer.parseInt(System.getProperty("port", "8007"));

public static void main(String[] args) throws Exception {
// Configure SSL.
final SslContext sslCtx;
if (SSL) {
SelfSignedCertificate ssc = new SelfSignedCertificate();
sslCtx = SslContextBuilder.forServer(ssc.certificate(), ssc.privateKey()).build();
} else {
sslCtx = null;
}

// Configure the server.
// 在官方的example中默认是Reactor主从多线程模式
// 1.配置线程池
EventLoopGroup bossGroup = new NioEventLoopGroup(1);
EventLoopGroup workerGroup = new NioEventLoopGroup();
final EchoServerHandler serverHandler = new EchoServerHandler();
try {
ServerBootstrap b = new ServerBootstrap();
b.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class) // 2.初始化channel类型
.option(ChannelOption.SO_BACKLOG, 100) // 2.1.设置channel参数
.handler(new LoggingHandler(LogLevel.INFO))
// 注册channelhandler,在netty中通过ChannelPipeline 去注册多个 ChannelHandler
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
public void initChannel(SocketChannel ch) throws Exception {
ChannelPipeline p = ch.pipeline();
if (sslCtx != null) {
p.addLast(sslCtx.newHandler(ch.alloc()));
}
//p.addLast(new LoggingHandler(LogLevel.INFO));
p.addLast(serverHandler);
}
});

// Start the server.
// 3.启动 通过bind() 方法会真正触发启动,sync() 方法则会阻塞
ChannelFuture f = b.bind(PORT).sync();
// Wait until the server socket is closed.
f.channel().closeFuture().sync();
} finally {
// Shut down all event loops to terminate all threads.
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
}
}
}
Read more »

0x00 阅读源代码

下载源代码

  • 阅读版本号 netty-4.1.33.Final
    • 由于github下载速度是在太慢了,这里用gitee克隆一个镜像。
    • 下载版本git clone -b netty-4.1.33.Final git@gitee.com:Z201/netty.git

查看源代码结构

  • 查看项目主要文件夹
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
tree -d  -L 1    
├── all
├── bom
├── buffer (Core netty定制的buffer)
├── codec (Protocol Support)
├── codec-dns (Protocol Support)
├── codec-haproxy (Protocol Support)
├── codec-http (Protocol Support)
├── codec-http2 (Protocol Support)
├── codec-memcache (Protocol Support)
├── codec-mqtt (Protocol Support 物联网)
├── codec-redis (Protocol Support )
├── codec-smtp (Protocol Support 邮件)
├── codec-socks (Protocol Support)
├── codec-stomp (Protocol Support ws)
├── codec-xml (Protocol Support)
├── common (Core)
├── dev-tools
├── docker
├── example (抄代码的地方)
├── handler (Protocol Support)
├── handler-proxy (Protocol Support)
├── license
├── microbench (测试用的)
├── resolver (Core)
├── resolver-dns (Core)
├── tarball
├── target
├── testsuite
├── testsuite-autobahn
├── testsuite-http2
├── testsuite-osgi
├── testsuite-shading
├── transport (Transport Services)
├── transport-native-epoll (Transport Servicesnative omitted - reserved keyword in Java)
├── transport-native-kqueue (Transport Servicesnative omitted - reserved keyword in Java)
├── transport-native-unix-common Transport Services native omitted - reserved keyword in Java)
├── transport-native-unix-common-tests
├── transport-rxtx (Transport Services 串口编程 作废)
├── transport-sctp (Transport Services )
└── transport-udt (Transport Services 作废)
  • 将项目导入idea中,笔者采用mac系统。这里需要注意需要安装部分软件。
1
2
3
4
5
6
brew install autoconf automake libtool
# 如果安装了国内的镜像出现Error opening archive: Failed to open
# export HOMEBREW_BOTTLE_DOMAIN=''
# 参考文档 https://zhuanlan.zhihu.com/p/383707713
# 如果是mac系统需要手动将pom文件tcnative.classifier修改成对应系统的。
mvn install -Dmaven.test.skip=true

0x01 根据官方文档快速入门

强烈推荐认真阅读netty的官方文档。

Core

  • netty-common模块是 Netty 的核心基础包,其他模块都需要依赖它。常用的包括通用工具类和自定义并发包。
  • netty-buffer 模块中Netty自己实现了的一个更加完备的ByteBuf 工具类,用于网络通信中的数据载体。
  • netty-resover模块主要提供了一些有关基础设施的解析工具,包括 IP Address、Hostname、DNS 等。

Protocol Support

  • netty-codec模块主要负责编解码工作,提供主流协议的编辑码,还提供了抽象编解码类 ByteToMessageDecoder 和 MessageToByteEncoder,通过继承这两个类我们可以轻松实现自定义的编解码逻辑。

Transport Service

  • netty-transport 模块可以说是 Netty 提供数据处理和传输的核心模块,如 Bootstrap、Channel、ChannelHandler、EventLoop、EventLoopGroup、ChannelPipeline 等。其中 Bootstrap 负责客户端或服务端的启动工作,包括创建、初始化 Channel 等;EventLoop 负责向注册的 Channel 发起 I/O 读写操作;ChannelPipeline 负责 ChannelHandler 的有序编排,这些组件在介绍 Netty 逻辑架构的时候都有所涉及。

OpenJdk 11 环境下JavaFx开发环境设置

pom依赖

1
2
3
4
5
6
7
8
9
10
11
12
<dependencies>
<dependency>
<groupId>org.openjfx</groupId>
<artifactId>javafx-controls</artifactId>
<version>11</version>
</dependency>
<dependency>
<groupId>org.openjfx</groupId>
<artifactId>javafx-fxml</artifactId>
<version>11</version>
</dependency>
</dependencies>

idea设置

下载javafx版本,然后引入依赖。项目lib中引入。启动的时候在vm中添加启动参数

1
2
3
4
5
6
7
8
9
10
11
12
--module-path
/Users/$USER/word/libhome/javafx-sdk-11.0.2/lib
--add-modules
javafx.controls
--add-modules
javafx.base
--add-modules
javafx.graphics
--add-modules
javafx.fxml
--add-modules
javafx.media

本章是整理知识内容,为强化知识长期更新。

Mysql基础架构图

mysql基础架构图

简单介绍上图相关模块功能

  • 连接器:管理连接,权限验证

  • 查询缓存:命中缓存则直接返回结果

  • 语法解析:词法分析,语法分析

  • 查询优化:执行计划生成,索引选择

  • 执行器:操作引擎,返回结果

  • 存储引擎:存储数据,提供读写结构。

连接器

我们需要连接到mysql服务端才能进行各种操作。

1
2
# 使用密码登录到时候我们会使用这种格式。
mysql -h$ip -P$port -u$user -p

执行上述命令会提示输入密码。输入完密码后就进入了mysql。

  • 如果用户名或者密码输入错误,会提示一个Access denied for user到错误,然后客户端程序结束执行。

  • 如果用户名和密码认证通过,连接器会去权限表中查询该用户拥有的所有权限。此后该连接里面的权限逻辑判断都会依赖认证成功时候读取到的权限。

因此当修改用户的权限之后,如果被修改用户的在登录状态是不会修改已经连接的权限。需要让被修改权限用户从新登录。

  • 实际上连接的方式有多种,上面通过密码的方式只是其中的一种方式。当客户端与服务端mysql进程进程建立连接,服务器的进程就会创建一个单独的线程来专门处理与这个客户端的交互,当该客户断开与服务端连接时,服务端并不会马上销毁对应的交互线程,而是缓存起来。当另一个新的客户端连接进来的时候,在把这个缓存的线程分配给新的客户端。这样就避免的频繁创建和修改线程,节省系统的开销。但是连接线程多了也会影响服务端,所以也有默认的参数限制客户端的连接数量。
1
2
# 这里是查询服务端存在的连接。
SHOW PROCESSLIST;

我使用的阿里云的数据库,这里展示下结果集。这里需要注意,登录的账号需要有PROCESS的权限否则只能看到自己的连接信息(线程),我这里采用的root所以看到全部到。

连接完成后如果没有执行后续操作则 Command会显示Sleep的状态,长时间Sleep会导致连接自动断开。默认的时间是8小时,也就是参数 wait_timeout。当客户端连接断开后,若客户端再次发出请求就会提示一个

Lost connection to MySQL server during query,此时就只能重新建立连接了。

半双工
  • MySQL客户端/服务端通信协议是“半双工”的:在任一时刻,要么是服务器向客户端发送数据,要么是客户端向服务器发送数据,这两个动作不能同时发生。一旦一端开始发送消息,另一端要接收完整个消息才能响应它,所以我们无法也无须将一个消息切成小块独立发送,也没有办法进行流量控制。
  • 服务器响应给用户的数据通常会很多,由多个数据包组成。但是当服务器响应客户端请求时,客户端必须完整的接收整个返回结果,而不能简单的只取前面几条结果,然后让服务器停止发送。因而在实际开发中,尽量保持查询简单且只返回必需的数据,减小通信间数据包的大小和数量是一个非常好的习惯,这也是查询中尽量避免使用SELECT *以及加上LIMIT限制的原因之一。
长连接与短连接
  • 在数据库里面,长连接是指数据库建立连接,如果客户端有持续的请求则使用同一个连接,短连接是指每次执行几次短的查询就断开连接,下次查询再次创建连接。由于创建的过程复杂,一般来说尽量减少创建连接的动作,尽可能使用长连接。特别是在开发的时候出现大伙突然连接不上测试库,此时就检查下连接是不是太多了。因为连接多了会导致内存消耗特别快。

    • mysql的执行过程中临时使用的内存管理是在连接线程对象里面,服务端断开的时候才会释放,为了避免内存消耗过大,每次执行较大的操作之后,可以通过mysql_reset_connection来重新初始化连接资源。这个操作是不需要重新做权限,只是恢复到创建的初始状态。
  • MySQL mysql_reset_connection 官方文档

查询缓存

一个不被建议使用的功能,在新版的8.0中已经被删除了。总结就是弊大于利。

  • 建立完成后,假设执行一次SELECT语句,执行逻辑就会到第二部查询缓存。mysql拿到一个查询之后,会先到缓存中寻找释放有完全对应的,因为之前执行的查询语句和结果集会直接被缓存起来,以Keys-value的形式。keys是查询语句-value是查询结果集。如果能命中这个key则直接返回value。咋一看挺有用的,但实际上查询缓存的实效的太频繁。并且keys的命中条件太不聪明了,如果两次查询语句在任何的字符上存在不通(空格、大小写)都不会缓存命中。为什么会频繁失效,因为如果对查询语句中的表进行更新,就会导致缓存失效。比如INSERT、UPDATE 、DELETE 、TRUNCATE TABLE 、ALTER TABLE 等等就会导致缓存失效。

语法解析

如果没有命中查询缓存,就要开始执行sql语句了。mysql需要做什么,因此需要对sql进行解析。MySQL通过关键字将SQL语句进行解析,并生成一颗对应的解析树。这个过程解析器主要通过语法规则来验证和解析。比如SQL中是否使用了错误的关键字或者关键字的顺序是否正确等等。预处理则会根据MySQL规则进一步检查解析树是否合法。比如检查要查询的数据表和数据列是否存在等等。

  • 本质上发送过来的是一个文本信息,这里就需要对该文本信息进行编译,涉及到词法解析语法解析语义解析等阶段。需要注意的是,这里会对sql语句进行一些检查,比如设计的相关表、表字段。这里检查通过的才会进入查询优化模块。
  • 根据SQL语言的功能可以划分成4个部分
    • DDL,英文叫做 Data Definition Language,也就是数据定义语言,它用来定义我们的数据库对象,包括数据库、数据表和列。通过使用 DDL,我们可以创建,删除和修改数据库和表结构。
    • DML,英文叫做 Data Manipulation Language,数据操作语言,我们用它操作和数据库相关的记录,比如增加、删除、修改数据表中的记录。
    • DCL,英文叫做 Data Control Language,数据控制语言,我们用它来定义访问权限和安全级别。
    • DQL,英文叫做 Data Query Language,数据查询语言,我们用它查询想要的记录,它是 SQL 语言的重中之重。在实际的业务中,我们绝大多数情况下都是在和查询打交道,因此学会编写正确且高效的查询语句,是学习的重点。

查询优化

通过之前的语法解析,基本可以判定语法树是合法的,此时mysql就知道文本内容要做什么。

  • mysql会对查询语句进行优化,优化的结果就是生成一个执行计划,这个计划会表明使用了那些查询索引,表之间的连接是什么样子的。有时候出现多种执行方式,只是效率不通,优化器会决定使用哪一个执行方案,这个时候就涉及到EXPLAIN语句,该语句可以查看sql到执行计划,这将涉及到查询优化。
  • MySQL的查询优化器是一个非常复杂的部件,它使用了非常多的优化策略来生成一个最优的执行计划。
    • 重新定义表的关联顺序(多张表关联查询时,并不一定按照SQL中指定的顺序进行,但有一些技巧可以指定关联顺序)
    • 优化函数
    • 提前终止查询(比如:使用Limit时,查找到满足数量的结果集后会立即终止查询)
    • 优化排序(在老版本MySQL会使用两次传输排序,即先读取行指针和需要排序的字段在内存中对其排序,然后再根据排序结果去读取数据行,而新版本采用的是单次传输排序,也就是一次读取所有的数据行,然后根据给定的列排序。对于I/O密集型应用,效率会高很多。

执行器

进入到执行sql到环节。

  • 通过之前到查询优化,进入执行阶段,此时会先判断该连接用户是否有相关表的权限,如果没有会返回权限错误。如果有权限就会打开表,执行器会根据表定义的引擎去使用这个引擎提供的接口完成流程。

存储引擎

关于存储引擎,这里明确提醒。不是三种也不是四种。而是多种因为随时可能出现新的引擎。

  • 我们常用的就是InnoDBMyISAM,其它的不常见就不做过多介绍。mysql现在默认的引擎是InnoDB所以主要也是了解InnoDB
1
2
# 查看当前服务支持的存储引擎
SHOW ENGINES;

Read more »

本章是整理知识内容,为强化知识长期更新。

面向对象思想

  • 面向对象是一个思想,时间万物皆可以被看做一个对象。

封装

  • 隐藏对象的属性和实现的具体细节,只对外暴露公共访问方式。

继承

  • 当多个类出现相同代码逻辑时,我们通常将相同的代码重构到一个类中,如果是绑定关系就可以使用继承。
  • Java中类是单继承。

多态

  • 一个事物的的多种状态,比如女人、男人都是人的性别。人的性别就分为女人、男人。

抽象

  • 在逻辑上看似相关的,想要把他们联系起来。这样可以提高效率。矩形、圆形,都可以具有周长和面积两个方法,但是计算的方式完全不同,矩形和圆形之间肯定不能构成子父类的关系,那么只能是同时去继承一个父类。这时,就引出了抽象的概念。

重写和重载

  • 重载:同一个类中,方法名相同,参数个数或者类型不相同,返回类型可以不相同。
  • 重写:类的继承关系中体现,子类重写父类的方法。
区别点 重载方法 重写方法
发生范围 同一个类 子类
参数列表 必须修改 一定不能修改
返回类型 可修改 子类方法返回值类型应比父类方法返回值类型更小或相等
异常 可修改 子类方法声明抛出的异常类应比父类方法声明抛出的异常类更小或相等;
访问修饰符 可修改 一定不能做更严格的限制(可以降低限制)
发生阶段 编译期 运行期
  • 重写(override)

    • 存在父类和子类之间。
    • 方法名、参数、返回值相同。
    • 方法被final修饰不能被重写。
    • 子类重写父类方法后,不能抛出比父类方法的异常。子类不能缩写父类的方法访问权限
  • 重载(overload)

    • 参数类型、个数、顺序至少有一个不相同。
    • 不能重载只有返回值不同的方法名。
    • 存在于父类和子类、同类中。
  • Constructor 不能被 override(重写),但是可以 overload(重载),所以你可以看到⼀个类中有多个构造函数的情况。

介绍下 Java 基本数据类型

  • Java中存在8个原生数据类型,同时又分成四种:整形、浮点型、char、Boolean。它们之间存在自动类型转换,规则是从小到大。并且都存在自动装箱拆箱特性,但是这种操作是隐式操作而且在某些情况会导致CG压力增大。
类型 存储需求 取值范围
int 4字节 -2 147 483 638 ~ 2 147 483 637
short 2字节 -32 768 ~ 32 767
long 8字节 -9 223 372 036 854 775 808 ~ 9 223 372 036 854 775 807
byte 1字节 - 128 ~ 127
  • 整型的范围与运行Java运行的硬件没有关系,所有的数据类型所占的字节数量与平台无关。
类型 存储需求 取值范围
float 4字节 大约 $\pm$ 3.402 823 37F + 38F (有效位数为7~8位)
double 8字节 大约 $\pm$ 1.797 693 134 862 315 70E + 308 (有效位数为15位)
  • double这种类型的精度是float的两倍。

  • 所有浮点数值计算都遵循IEEE 754规范,下面是溢出和出错的情况的三种特殊的浮点数值。

    • 正无穷大
    • 负无穷大
    • NaN ( 不是一个数字 )
    • 一个整整数除以0的结果为正无穷大,计算0/0或者负数的平方根结果为NaN。
  • char类型

    • char类型表示单个字符,属于Unicode编码表。因为历史原因,不建议在程序中使用。除非确实要对UTF-16代码单元进行操作。
    • char字节大小
      • Java中无论是汉字还是英文字母都是用Unicode编码来表示的,一个Unicode是16位,每字节是8位,所以一个Unicode码占两字节。但是英文字母比较特殊,源自于8位(1字节)的ASCII吗,于是在Unicode码仅使用了低8位(1字节)就可以表示。
  • boolean类型

    • 布尔类型,只有两个值false、true。基本用于判定条件。
    • boolean字节大小
      • Java规范中并未明确规定boolean类型的大小。
  • 自动类型转换

    • 整型、实型(常量)、字符型数据可以混合运算。运算中,不同类型的数据先转化为同一类型,然后进行运算。

      转换从低级到高级。

      1
      byte,short,char—> int —> long—> float —> double
    • 不能对boolean进行类型转换、不能把对象类型转换成不相关的对象、把大容量的对象转换成小容量对象时需要强制类型转换、转换过程中间可能出现精度损失。

装箱和拆箱boxing or unboxing

装箱:将基础类型用它们对应类型包装起来

拆箱:将包装类型转换成基本数据类型

原语 对应的JDK类
int java.lang.Integer
short java.lang.Short
long java.lang.Long
byte java.lang.Byte
char java.lang.Character
double java.lang.Double
float java.lang.Float
boolean java.lang.Boolean
  • Java中只有原生数据类型是特殊的,它们不是对象。其它的都是对象。那么就一个尴尬的问题,集合类都是存放的对象,JDK5之后考虑到这个问题就自动进行逆行拆箱装箱的操作。

    1
    2
    3
    //比如所在泛型中是不能存放原生数据类型的,如要要存放原生数据类型的数据,需要装箱。
    Collection<int> c = new ArrayList<int>(); //这是无法编译成功的。
    Collection<Integer> cc = new ArrayList<Integer>(); //这样才行。
  • 每个 JDK 类都提供了相应方法来解析它的内部表示,并将其转换为相应的原语类型。

  • 但是注意装箱拆箱操作其实是非常消耗内存的举动,在该过程中可能会生成生成无用对象增加GC压力。所以尽量避免这中操作。

    1
    2
    3
    4
    Integer sum = 0;
    for(int index = 1000; index < 5000; index ++){
    sum+=index;
    }

    比如这种,每次sum都需要自动拆箱。

  • 默认情况下整数的类型都是int、浮点型的数都是double。

    1
    float d = 1.1f; //在后面添加f,大小写不区分。隐式强制类型转换
Read more »