个人笔记记录

Java预科

冯诺依曼体系结构

冯诺依曼体系结构

基本的DOS命令

打开CMD的方式

  1. 开始+系统+命令提示符

  2. Win键+R输入cmd打开控制台(推荐使用)

  3. 在任意的文件来下面,按住 shift键+鼠标右键点击,在此处打开命令行窗口

  4. 资源管理器的地址输入:cmd + 空格 + 绝对路径(在运行代码文件的时候有用)

以管理员身份运行CMD

​ 按Ctrl+Q进入开始菜单的搜索栏,输入cmd,点击以管理员身份运行

常用的DOS命令

# 盘符切换 
D:
C:
E:

# 查看当前目录下的所有文件 (directory)
dir

# 切换目录 (change directory)
cd /d 完整路径

# 进入当前的上一级目录
cd ..

# 进入当前的下一级目录
cd 文件夹

# 清理屏幕 (clear screen)
cls

# 退出终端
exit

# 查看电脑ip配质 (ip configure)
ipconfig

#打开应用
    # 计算器 (calculator)
    calc
    # 画图工具 (Microsoft painter)
    mspaint
    # 记事本
    notepad
    
# 网络命令
    # ping命令 (测试网络是否互通,粗略判断传输质量)
    ping www.baidu.com
    # netstat命令 (查看各端口网络连接情况)
    netstat -ano
    # -a : 显示所有活动的 tcp 连接,以及计算机监听的 tcp 和 udp 端口
    # -n : 只以数字形式显示所有活动的 tcp 连接的地址和端口号
    # -o : 显示活动的 tcp 连接并包括每个连接的进程id (PID)
    # 查找使用给定端口的活动连接和PID
    netstat -aon|findstr "1080"
    # 显示所有正在运行的进程
    tasklist
    # 查找给定PID对应的运行中的进程
    tasklist|findstr "4568"
    # 终止指定的进程
    taskkill /f /im 进程名称
    taskkill /f /pid 进程PID
    # /f : 强制终止,有时会缺少权限无法强制终止,需要管理员权限运行
    
# 文件操作
	# 新建目录 (make directory) 
	md 目录名
	# 移除目录 (remove directory)
    rd 目录名
    # 新建文件
    cd> 文件名
    # 删除文件
    del 文件名

注意DOS不区分大小写

注意路径分隔符是\,而参数命令的标识符是/

如何路径中含有空格,需要用双引号""将整个路径括起来

在DOS中不要用ctrl+c来复制ctrl+v来粘贴,因为ctrl+c会退出当前命令,用鼠标左键框选内容后,鼠标单击右键即可复制,再用鼠标单击右键即可粘贴

计算机语言

第一代语言

机器语言

- 基本计算方式是基于二进制的方式
- 这种代码是直接给计算机使用的,不经过任何转换

第二代语言

汇编语言

- 解决人类无法读懂机器语言的问题
- 指令替代二进制
- 应用:机器人,病毒,逆向工程 (对目标产品进行逆向分析,得出该产品的各项设计流程和要素,从而做出相近但不完全一样的产品)

第三代语言

- 摩尔定律 (集成电路上可以容纳的晶体管数目在大约每经过18个月便会增加一倍。换言之,处理器的性能每隔两年翻一倍。)

高级语言

大体上分为面向过程和面向对象的语言

例如C语言是典型的面向过程的语言,C++、JAVA是典型的面向对象的语言

发展历程:C语言 - C++ - JAVA - C# - Python, PHP, JavaScript

Java入门

Java三大版本与JDK JRE JVM

JavaSE = java standard edition

JavaME = java micro edition

JavaEE = java enterprise edition

其中JavaME基本已经无人使用

JDK = Java Development Kit

JRE = Java Runtime Environment

JVM =Java Virtual Machine

三者是包含与被包含关系

JDK, JRE, JVM的关系

安装开发环境

在oracle官网搜索JDK8,注册账户同意协议并下载安装

(因为企业基本用的都是JDK8,不要下载最新的JDK13或者15)

注意选中取消公共JRE的安装

(因为JDK已经有一个专用JRE了)

配置环境变量:

​ 系统变量:名称—JAVA_HOME,值—安装目录

​ 系统变量下的Path:增加 %JAVA_HOME%\bin 和 %JAVA_HOME\jre\bin

测试 JDK:打开cmd输入java -version

另外,建议下载Notepad++作为文本编辑器

Notepad++默认是单实例运行的(会打开上次未关闭的所有文件),如果想更改成记事本那样的多实例需要在设置里面更改

此外,设置.java文件的默认打开方式为notepad++,方法:win10设置文件默认打开方式为notepad++,其中不设置兼容性也可,只要右键以管理员身份打开即可


安装目录介绍:

​ bin文件夹(binary):存放二进制可执行程序exe

​ include:存放c和c++的头文件,因为jdk是用c和c++编写的

​ jre:Java运行环境

​ lib(library):存放Java的类库文件

​ src.zip:存放Java类

HelloWorld

新建一个Code文件夹用于存放代码

在文件夹下新建一个Hello.txt文件后缀名改为.java

用notepad++打开输入以下代码:

public class Hello{
	public static void main(String[] args){
		System.out.print("Hello, world!");
	}
}

在Code文件夹下打开cmd

输入javac Hello.java

输入java Hello

即可运行成功

含义:

​ javac:是编译命令,将java源文件编译成同名的class字节码文件

​ 例如:javac Hello.java将在java文件的同目录下生成Hello.class文件

​ java:是运行class字节码文件,由java虚拟机对字节码进行解释和运行

​ 例如:java Hello将成功解释运行Hello.class文件,打印Hello, world!

注意点:

  1. 注意java命令后面的文件名不用加.class后缀,已经默认是.class后缀了
  2. java是大小写敏感的,所以javac和java命令内容一定要分清大小写
  3. 代码内容尽量使用英文,有些缺少GBK支持可能报错
  4. 注意类名必须和文件名一样,并且首字母大写

编译型和解释型

计算机运行代码需要将源代码转换成机器码,也就是二进制指令,根据转换的时机不同,编程语言可分为编译型和解释型

  • 编译型:必须提前将所有源代码一次性转换成二进制指令,也就是生成一个可执行程序,比如C语言、C++等
  • 解释型:可以一边执行一边转换,需要哪些源代码就转换哪些源代码,不会生成可执行程序,比如 Python、JavaScript、PHP、Shell、MATLAB等
  • Java和 C#是一种比较奇葩的存在,它们是半编译半解释型的语言,源代码需要先转换成一种中间文件(字节码文件),然后再将中间文件拿到虚拟机中执行
编译型语言和解释型语言的执行流程

优缺点:

编译型:

​ 对于编译型语言,开发完成以后需要将所有的源代码都转换成可执行程序,比如 Windows 下的.exe文件,可执行程序里面包含的就是机器码。只要我们拥有可执行程序,就可以随时运行,不用再重新编译了,也就是“一次编译,无限次运行”。

​ 在运行的时候,我们只需要编译生成的可执行程序,不再需要源代码和编译器了,所以说编译型语言可以脱离开发环境运行。

​ 编译型语言一般是不能跨平台的,也就是不能在不同的操作系统之间随意切换。

​ 编译型语言不能跨平台表现在两个方面:

​ 1) 可执行程序不能跨平台:因为不同操作系统对可执行文件的内部结构有着截然不同的要求(windows是.exe,linux是.elf)

​ 2) 源代码不能跨平台:不同平台支持的函数、类型、变量等都可能不同


解释型:

​ 对于解释型语言,每次执行程序都需要一边转换一边执行,用到哪些源代码就将哪些源代码转换成机器码,用不到的不进行任何处理。每次执行程序时可能使用不同的功能,这个时候需要转换的源代码也不一样。

​ 因为每次执行程序都需要重新转换源代码,所以解释型语言的执行效率天生就低于编译型语言,甚至存在数量级的差距。计算机的一些底层功能,或者关键算法,一般都使用 C/C++ 实现,只有在应用层面(比如网站开发、批处理、小工具等)才会使用解释型语言。

​ 在运行解释型语言的时候,我们始终都需要源代码和解释器,所以说它无法脱离开发环境。

​ 当我们说“下载一个程序(软件)”时,不同类型的语言有不同的含义:

​ 1) 对于编译型语言,我们下载到的是可执行文件,源代码被作者保留,所以编译型语言的程序一般是闭源的。

​ 2) 对于解释型语言,我们下载到的是所有的源代码,因为作者不给源代码就没法运行,所以解释型语言的程序一般是开源的。

​ 相比于编译型语言,解释型语言几乎都能跨平台,“一次编写,到处运行”是真是存在的,而且比比皆是。解释型语言能跨平台要归功于解释器。

​ 我们所说的跨平台,是指源代码跨平台,而不是解释器跨平台。解释器用来将源代码转换成机器码,它就是一个可执行程序,是绝对不能跨平台的。

​ 官方需要针对不同的平台开发不同的解释器,这些解释器必须要能够遵守同样的语法,识别同样的函数,完成同样的功能,只有这样,同样的代码在不同平台的执行结果才是相同的。

​ 你看,解释型语言之所以能够跨平台,是因为有了解释器这个中间层。在不同的平台下,解释器会将相同的源代码转换成不同的机器码,解释器帮助我们屏蔽了不同平台之间的差异。

Java程序运行机制

img

这里的类装载器就是JVM,字节码校验器检查语法错误

使用IDEA开发

IDE:集成开发环境,Integrated Development Environment,是用于提供程序开发环境的应用程序,例如eclispe、idea、visual studio

IDEA一般被认为是最好的Java开发工具之一,公司是JetBrains,和PyCharm同一个公司

百度搜索idea进入JetBrains官网,选择IntelliJ IDEA下载Ultimate版本(收费但是可以破解,参考三种方法),不要下载community版本,因为后面的课程会用到JavaScript和SQL,而community版本虽然免费但是是没有这些功能的

安装时只勾选三项64-bit launcher, Add "Open Folder as Project", .java,另外不要放在C盘因为会越用越大


新建Project时选中Java,SDK选择安装的JDK版本(一般会自动锁定)

新建Hello的project

在项目文件夹的src子文件夹下(类文件夹)新建Java Class(.java文件)

输入psvm(缩写)或者main都可以自动补足为public static void main(String[] args){}

输入sout可以补足为System.out.println();

(println和print的唯一区别就是最后会换行,等同于print("xxx\n"),printf和C中的printf一样格式化输出)

与PyCharm不同,在代码行数的绿色▶按钮即可运行.java文件

设置优化IDEA,参考IDEA的常见的设置和优化(功能)IntelliJ IDEA 性能优化

其中优化类注释和方法注释:最好参考IDEA类和方法注释模板设置(非常详细),其中Param的default value改为

groovyScript("def result=''; def params=\"${_1}\".replaceAll('[\\\\[|\\\\]|\\\\s]', '').split(',').toList(); for(i = 0; i < params.size(); i++) {if(i==0){result+= params[i] + ((i < params.size() - 1) ? '\\n' : '')}else{result+=' * @param  ' + params[i] + ((i < params.size() - 1) ? '\\n' : '')}}; return result", methodParameters()) 

return的值可以改成在expression处选中methodReturnType()

另外不建议方法注释的快捷键是Enter,最好用Tab,因为Enter会让多行注释不方便换行

快捷键

  1. Ctrl + [ 和 Ctrl + ] 分别是匹配当前指针位置的上一级左大括号和右大括号,在代码特别长的时候很有用
  2. CTRL + SHIFT + ENTER 在当前行任何地方可以快速在末尾生成分号,并且将代码规范化(添加空格)
  3. CTRL + D 是复制当前行并粘贴到下一行(Duplicate)CTRL + Y 是删除当前行,在IDEA里面的Redo被换成了Ctrl + Shift + Z
  4. ALT + ENTER 是引入(补全)局部变量,例如在Math.pow(2, 3);前后或者中间按下快捷键,就会变成double pow = Math.pow(2, 3);
  5. 按住CTRL鼠标左键点击工具类,可以进入类的源码,点击包,可以在左侧进入包的源路径

Java基础

注释

新建项目流程:

  1. 一般新建项目不要选择Java而是选择Empty空项目
  2. 而后从菜单新建module模块,选中Java和对应的SDK
  3. 最后从工具栏或者菜单栏打开Project Structure - Project,选择SDK和对应的Language Level(也就是8)
  4. 最后在module的src里面新建Java Class开始编写

要养成书写注释(Comments)的好习惯,不然不仅是别人看不懂,自己过几天也会看不懂,增加时间成本

单行注释(Line Comments)://开头,快捷键Ctrl + /

多行注释(Block Comments):/*开头,*/接尾,快捷键Ctrl + Shift + /

文档注释(JavaDoc):/**开头再按Enter,快捷键/* + Tab(只有按照上一节使用IDEA开发设置了优化方法注释才可以)

注意区分注释(Comments)和注解(Annotations),注解是会被读取运行的,注释不会

标识符和关键字

标识符

1、定义

在Java程序中,所有的由程序员自己命名的元素统称为“标识符”

2、标识符命名规则

标识符由数字、字母、_$组成

标识符不能以数字开头

标识符区分大小写

标识符不能是关键字

注意:

定义类名的标识符,首字母必须大写,后面的单词首字母大写,遵循“大驼峰命名法”

(例如XxxXxxx)

定义方法标识符:首字母尽量小写,后面的单词首字母大写,遵循“小驼峰命名法”

(例如 xxxXxx)

定义变量的标识符:同小驼峰

定义包名:按模块分层级,使用公司的域名倒写,(例如 com.j2008.xxx)

定义项目名:尽量使用英文(Java项目可以使用中文,JavaWeb项目一定使用英文)

所有标识符尽量使用英文,不建议用拼音和中文

关键字

1、定义

Java关键字是电脑语言里事先定义的,有特别意义的标识符,有时又叫保留字,还有特别意义的变量。Java的关键字对Java的编译器有特殊的意义,他们用来表示一种数据类型,或者表示程序的结构等,关键字不能用作变量名、方法名、类名、包名和参数,一共有50个关键字(48关键字+2保留字),关键字都是小写的英文单词。

2、Java的50个关键字

src=http___pic2.zhimg.com_v2-8408d6adff55ca7f1772f666afae9425_b.png&refer=http___pic2.zhimg

数据类型

强类型语言:一种强制类型定义的语言,一旦某一个变量被定义类型,如果不经过强制转换,则它永远就是该数据类型了,强类型语言包括Java、.net 、Python、C++等语言

例子:定义了一个整数,如果不进行强制的类型转换,则不可以将该整数转化为字符串

弱类型语言:一种弱类型定义的语言,某一个变量被定义类型,该变量可以根据环境变化自动进行转换,不需要经过显性强制转换。弱类型语言包括vb 、PHP、JavaScript等语言

例子:

var A =5;
var B = "5";
SumResult = A +B;
MinResult = A -B;

输入SumResult的答案不是10,而是55,再次是将A的类型转化为了字符串,然后进行拼接。输入MinResult的答案是0,是将B的类型转化为了数字,然后进行减法。

Java的数据类型分为基本类型引用类型两大类

基本类型

(primitive type)

(笔试常考)

img

补充:其中boolean类型占1位

其中整数类型常用int(也是默认),浮点数类型常用double(也是默认)

范围:long>int>short>byte,double>float

注意定义long和float变量的时候,需要在赋值的数字后面分别加上L和F,也可以在赋值的数字前面分别加上(long)和(float)做强制转换但是不推荐(因为不是所有东西都可以强制转换)

因为赋值的数字默认是int(整数)和double(小数),而赋值的时候被赋值的变量和赋值的变量(包括数字和字符串)需要是同一数据类型,例如不能将数字赋值给String变量,而Java只会自动将范围小的数据类型转为范围大的(对于int常数来说称为**Java的常量优化机制**),反过来转换需要强制转换(因为有舍入误差)

如果不加L,当long变量赋值的数字在int范围内时,虽然可以将数字自动转换为long类型,再赋值给long变量;但是当数字超出int范围时,Java会认为这个数字不符合int类型,这个数字的数据类型不明确(数字本身就是一种定义,默认为int),无法进行变量定义,此时则需要在数字后面加上L,强制转换成一个long类型的数字,才能再赋值

如果不加F,当float变量赋值的数字是整数在int范围内时,虽然由于float取值范围大于int,可以把数字自动转换成float再赋值;但是如果float变量赋值的数字是浮点数时,浮点数默认是double类型,而float的范围又小于double,由于只会自动将范围小的数据类型转为范围大的,所以浮点数不会自动转换成float,无法与被赋值的float变量匹配,因此无法正常定义;此外,如果float变量赋值的数字是整数但是超出int范围时,同样Java会认为这个数字不符合int类型,这个数字的数据类型不明确,无法进行变量定义,此时则需要在数字后面加上F,强制转换成一个float类型的数字,才能再赋值

注意:Java的常量优化机制只有当等号右边仅含常量时才会生效,右边含有变量是不生效的

例子:

public class Demo1 {
    public static void main(String[] args) {
        float a = 3.14F;
        long b = 1000000000000L;
        byte c = 10 + 30;
        System.out.println(getType(a));
        System.out.println(getType(b));
        System.out.println(getType(c));
    }
    private static String getType(Object a) {
        return a.getClass().toString();
    }
}

//以下为错误示范,这种情况下常量优化机制是不生效的
public class Demo1 {
    public static void main(String[] args) {
        int a = 10;
        byte b = a + 30;
    }
}

另外注意1e3和1000是不一样的,1e3 = 1.0*Math.pow(10,3),默认是double类型,1000默认是int类型

char是字符,String才是字符串,char只能定义一个字(不能空可以空格)且必须单引号不能双引号,String不是关键字是一个类(可以空),且只能双引号不能单引号

boolean是布尔值,只能赋值true或false

public class Demo1 {
    public static void main(String[] args) {
        char firstname = '陈';
        String lastname = "灿东";
        boolean male = true;
        boolean female = false;
    }
}

引用类型

(reference type)

img

这里先知道有哪些引用类型即可,后续会慢慢讲到

字节

位(bit):是计算机内部数据儲存的最小単位,11001100是ー个8位二进制数

字节(byte):是计算机中数据处理的基本单位,习惯上用大写B来表示

1 B(byte字节)= 8 bit(位)

字符:是指计算机中使用的字母、数字、字和符号

例子:1个byte类型数据占1 B = 8 bit,也就是1个7位二进制数字,加上一个1位表示正负号

  • 1bit表示1位
  • 1Byte表示一个字节
  • 1B=8b
  • 1024B=1KB
  • 1024KB=1M
  • 1024M=1G

拓展:

32位系统和64位系统的含义

1、32位和64位意思是处理器一次能处理的最大位数;

2、32位系统的最大寻址空间是2的32次方=4294967296(bit)= 4(GB)左右;

3、64位系统的最大寻址空间为2的64次方=4294967296(bit)的32次方,数值大于1亿GB。

32位系统和64位系统的区别:

1、CPU要求不同。CPU有32位和64位之分,32位的CPU只能安装32位系统,而64位的CPU既可以安装32位系统也可以安装64位系统。

2、运算速度不同。64位CPU的指令集可以运行64位数据指令,比32位CPU提高了一倍。

3、寻址能力不同。32位系统的处理器最大只支持到4G内存,而64位系统最大支持的内存高达亿位数,实际运用过程中大多数的电脑32位系统最多识别3.5GB内存,64位系统最多识别128GB内存。

4、软件兼容性不同。64位系统比32位系统的软件少,主要是64位系统推出的时间不长,所以64位系统的兼容性不如32位,虽然可以兼容32位软件,但是部分32位的软件在64位系统无法运行,但是目前的Win7 64位系统兼容性好了很多。

数据类型扩展及面试题讲解

整数拓展

进制 二进制 八进制 十进制 十六进制
前缀 0b 0 0x

注:十六进制的数字是09和AF

public class Demo1 {
    public static void main(String[] args) {
        int a = 0b11;
        int b = 017;
        int c = 19;
        int d = 0x1F;
        System.out.println(a);// 1*2+1
        System.out.println(b);// 1*8+7
        System.out.println(c);// 1*10+9
        System.out.println(d);// 1*16+15
    }
}

浮点数拓展

最好完全避免使用浮点数进行比较

因为浮点数能表示的字长是有限的(有些实数是无限的)且也是离散的(不是连续取值的)

浮点数存在舍入误差,只是接近但不等于

当需要比较小数的时候,一般会使用一种数学工具类BigDecimal进行比较(以后会学到),或者允许二者存在极小误差判断Math.abs(a-b) < 1E-6f

public class Demo1 {
    public static void main(String[] args) {
        float a = 0.1f;
        double b = 1.0/10;
        System.out.println(a==b);// false
        System.out.println("0b"+Long.toBinaryString(Double.doubleToRawLongBits(b)));
        System.out.println("0b"+Long.toBinaryString(Float.floatToRawIntBits(a)));
        System.out.println(Math.abs(a-b) < 1E-6f);// true
        System.out.println("====================");
        float c =232323232322323f;
        float d = c + 1;
        System.out.println(c==d);// true
        System.out.println("0b"+Long.toBinaryString(Float.floatToRawIntBits(c)));
        System.out.println("0b"+Long.toBinaryString(Float.floatToRawIntBits(d)));
    }
}
/*
浮点数a和b的二进制存储格式为
a:0b11111110111001100110011001100110011001100110011001100110011010
b:0b111101110011001100110011001101
十进制转二进制可能会产生无限数(即使十进制时是有限数)
因此由于4字节和8字节长度差异,a和b存在截断误差
浮点数c和d和二进制存储格式为
c:0b1010111010100110100101111111000
d:0b1010111010100110100101111111000
因为:
1 浮点数存储时,按照符号,指数,位数进行存储。
2 先将10进制数,转换成对应的二进制小数。
3 然后将小数点移至尾数第一个有效数字的右边,如果数字太大,小数点左移动得多,科学计数法指数变大,尾数变多。
4 当尾数太大,超过float或者double尾数表示范围时,将要做截断操作。
5 一个超出表示范围的大数,再加一后。此时加一很有可能被截断。
6 所有c和d没有误差
*/

详细的浮点数内存存储格式和过程:Java浮点型精度丢失问题

浮点数内存存储原理:Java 浮点数是如何存储的


此外不能使用浮点数作为循环变量

例如:精度问题会导致 (float)2000000000 == 2000000050为true,所以如下的循环不会执行:

for (float f = (float) 2000000000; f < 2000000050; f++) {
    ...
}

二进制和十进制互相转换

二进制数转换成十进制数

由二进制数转换成十进制数的基本做法是,把二进制数首先写成加权系数展开式,然后按十进制加法规则求和。这种做法称为"按权相加"法。

例如把二进制数 110.11 转换成十进制数。

img

十进制数转换为二进制数

十进制数转换为二进制数时,由于整数和小数的转换方法不同,所以先将十进制数的整数部分和小数部分分别转换后,再加以合并。

  • 十进制整数转换为二进制整数

十进制整数转换为二进制整数采用"除2取余,逆序排列"法。具体做法是:用2去除十进制整数,可以得到一个商和余数;再用2去除商,又会得到一个商和余数,如此进行,直到商为零时为止,然后把先得到的余数作为二进制数的低位有效位,后得到的余数作为二进制数的高位有效位,依次排列起来。

例如把 (173)10 转换为二进制数。

解:

img
  • 十进制小数转换为二进制小数

十进制小数转换成二进制小数采用"**乘2取整,顺序排列"**法。具体做法是:用2乘十进制小数,可以得到积,将积的整数部分取出,再用2乘余下的小数 部分,又得到一个积,再将积的整数部分取出,如此进行,直到积中的小数部分为零,或者达到所要求的精度为止。

然后把取出的整数部分按顺序排列起来,先取的整数作为二进制小数的高位有效位,后取的整数作为低位有效位。

例如把(0.8125)转换为二进制小数。

解:

img

字符拓展

所有字符的本质还是数字

最早的ASCII编码的范围是0~127,只包含大小写字母、数字和一些符号

中文字非常多1个字节不够,至少需要2个字节,中国因此制定了GB2312编码

可以想得到的是,全世界有上百种语言,日本把日文编到Shift_JIS里,韩国把韩文编到Euc_kr里,各国有各国的标准,就会不可避免的出现冲突,结果就是,在多语言混合的文本中,显示出来就会有乱码。

因此,Unicode应运而生。Unicode把所有的语言都统一到一套编码里,这样就不会再有乱码问题了。

Unicode标准也在不断发展,但最常用的是用2个字节表示1个字符(偏僻字符需要4个字节)。现代操作系统和大多数编程语言都直接支持Unicode。

新的问题又出现了:如果统一成Unicode编码,乱码问题从此消失了。但是,如果你写的文本基本上全是英文的话,用Unicode编码比ASCII编码需要多一倍的存储空间,在存储和传输上就十分不划算。

所以,本着节约的精神,又出现了把Unicode编码转化为“可变长编码”的UTF-8编码。UTF-8编码把一个Unicode字符根据不同的数字大小编码成1-6个字节,常用的英文字母被编码成1个字节,汉字通常是3个字节,只有很生僻的字符才会被编码成4-6个字节。如果你要传输的文本包含大量英文字符,用UTF-8编码就能节省空间。

UTF-8有个额外的好处,就是ASCII编码实际上可以被看成是UTF-8的编码的一部分,所以,大量只支持ASCII编码的历史遗留软件可以在UTF-8编码下继续工作。

img

ASCII、Unicode和UTF-8的关系是:Unicode是ASCII的扩展,UTF-8是Unicode的具体实现之一(unicode transformation format,还有utf-16和utf-32)

Unicode编码一般是2字节,表的数字范围是0 ~ 65535 (21612^{16}-1),一般通过转义来表示U0000 ~ UFFFF (16进制)

Unicode编码可以用双引号""也可以用单引号

public class Demo1 {
    public static void main(String[] args) {
        char c1 = 'a';
        char c2 = 'A';
        System.out.println(c1);
        System.out.println((int)c1);// 强制转换数据类型为int 97
        System.out.println(c2);
        System.out.println((int)c2);// 强制转换数据类型为int 65
        char c3 = '\u0061';// 0x0061 = 97
        System.out.println(c3);
    }
}

  • 转义字符

首先很重要一点,转义字符是供编译器识别的。当java文件编译成.class文件后就不存在转义一说了。

比如,定义一个字符串String a值为双引号“。由于java的语法规定,字符串字面量是要由一对双引号括起来。如果这样定义String a = " " ";编译器肯定识别a为空字符串,后面不成对的双引号还会报错。这时候需要一个转义字符(java选择了\)来标识中间的双引号是字符串值,而不是语法中用来包裹字符串的特殊字符。而编译后的class文件保存值“到常量池中,不存在歧义的问题(具体可以看关于class文件讲解的文章)。

一。八进制转义序列:用于表示ascii中的字符

规则: \+1到3位8进制数字;范围'\000'~'\177'

编译器是会自动识别转义符号\后可用的数字。

比如:"\402" 超过了最大值177,编译器就会识别为改字符串为空格加上一个字符”2“,再比如”\092",9不在8进制数字中,编译器就是识别为\0和字符“92”

二。unicode转义字符:用于表示unicode字符集中的字符

规则:\u + 4个十六进制数字;范围0~65535

必须要写全4个数字,即只能写\u0000而不能写\u0

三。特殊字符:单引号'、双引号"、反斜线\

java字符串定义中,双引号必须转义,即必须这样写:"\"";而单引号可不转义,即可以这样写" ' ";

相同的字符定义中,单引号必须转义,' \' ';双引号可不转义' " ';

反斜线不管作为字符还是字符串的值都需转义

四。控制字符

\r回车

\n换行

\f走纸换页

\t横向挑格

\b退格

\r移动到本行开头

\0空字符

这里需要注意,unicode转义字符和其他转义类型,转义的位置不同,编译器在将程序解析成各种符号之前,先将Unicode转义字符转换成为它们所表示的字符。可以理解为==编译器先将源文件中unicode转义字符转成对应字符再解析代码意义==。也就是unicode转义字符只是字符的另一种写法,即\u0022(双引号)只改变u0022的字面含义,不改变它所代表的双引号的代码含义。

这就导致有些看起来语法是错的语句但实际上可以编译运行,例如

System.out.println("\u0022.length());
// 看起来显然缺少一个双引号,但是由于\u0022本身就是双引号,因此可以编译运行

反之,也会导致一些看起来没问题的语句报错,例如

System.out.println("\u0022");  报“未结束字符串”错误

unicode转义字符有点类似于SAS的宏变量,替换过程发生在编译代码之前

拓展(以后会讲到)

public class Demo1 {
    public static void main(String[] args) {
        String sa = new String("hello world");
        String sb = new String("hello world");
        System.out.println(sa==sb);// false
        String sc = "hello world";
        String sd = "hello world";
        System.out.println(sc==sd);// true
    }
}

当"=="比较的是基本数据类型时,比较的是数值是否一致;当比较的是引用数据类型,则比较的是对象的地址是否一致。这里new String表示根据已有的字符串original来创建String对象,会创建一个新的内存地址,不管字符串是否相同,所以二者不一致。

布尔值拓展

一般只有新手才会写if (flag == true) {}这种语句,因为它完全等价于 if (flag){},此外注意与Python不一样,不可以写if (flag==1){},因为在Java中boolean true和false不对应任何数值,非要对应只能自己自定义函数

public class Demo1 {
    public static void main(String[] args) {
        boolean flag =true;
        if (flag == true){
            System.out.println("rookie");
        }
        if (flag){
            System.out.println("veteran");
        }
    }
}

调试

在代码左侧行数右侧设置断点之后,点击Debug (Shift+F9) 按钮(小虫子图样)即可调试代码,会运行代码直到断点行,并显示参数和变量的变化过程,注意断点所在行是不会运行的

类型转换

由于Java是强类型语言,所以在进行一些运算的时候需要进行类型转换

运算中,不同类型的数据先转化为同一类型,然后再运算

img
从左到右依次 : 数据表示的范围从小到大

强制类型转换

强制转换的格式:(类型)变量名

一般是从大范围到小范围进行转换(因为通常伴有截断误差)

例如

public class Demo1 {
    public static void main(String[] args) {
        int i = 128;
        byte b = (byte)i;// 内存溢出
        // 强制转换 (类型)变量名
        System.out.println(i);// 128
        System.out.println(b);// -128
    }
}

其中由于byte类型的范围是-128 ~ 127,因此内存溢出

即128 = 127 + 1 = 0111 1111(原码)+ 0b 0000 0001(原码) = 0b 0000 0001(补码) + 0b 0111 1111(补码) = 0b 1000 0000(补码)

在计算机中所有数都是以补码形式表示和储存的,具体机制参考原码、反码、补码知识详细讲解

  • 8位二进制数的取值范围为[1111 1111 , 0111 1111](原码) = [1000 0000 , 0000 0000](反码) = [-127 , 127],其中+0原码(0000 0000)和-0原码(1000 0000)的取值不一样
  • 而使用补码时,0的值唯一等于0000 0000(原码&补码),原本-0的补码1 0000 0000截断后同样等于000 0000(补码),[1000 0000](补码)则用来表示-128
    • -128 = (-1) + (-127) = [1000 0001]原 + [1111 1111]原 = [1111 1111]补 + [1000 0001]补 = [1000 0000]补(1 1000 0000截断后)
  • 因此用补码表示的8位二进制数的取值范围是[1000 0000 , 0111 1111](补码) = [-128 , 127]
  • 只有补码可以带符号位一起运算
  • -128没有反码和原码表示,1000 0000(补码)计算的原码是0000 0000(原码),但其实是不对的,因为把-0和+0区分开了

因此计算机将内存溢出的128认为是127+1,计算得到的补码是1000 0000(补码),即为-128

自动类型转换

只能是从小范围到大范围进行转换

public class Demo1 {
    public static void main(String[] args) {
        int i = 128;
        double b = i;
        System.out.println(i);// 128
        System.out.println(b);// 128
        // 精度误差
        System.out.println((int)28.7);// 28
        System.out.println((int)-45.89f);// 45
    }
}

注意点

  1. 不能对布尔值进行转换
  2. 不能把对象类型转换为不相干的类型
  3. 在把高容量转换到低容量的时候,强制转换
  4. 转换的时候可能存在内存溢出,或者精度问题
public class Demo1 {
    public static void main(String[] args) {
        char a = 'a';
        int b = a + 1;
        System.out.println(b);// 98
        System.out.println((char)b);// b
    }
}

溢出问题

操作比较大的数字时,注意溢出问题

解决方法:定义成容量更大的数据类型,并将运算表达式中任意一处数字强制转换成该类型

public class Demo1 {
    public static void main(String[] args) {
        // JDK7的新特性,数字中间可以用下划线分割
        int money = 1_000_000_000;
        int years = 20;
        int total1 = money * years;// -1474836480 
        long total2 = money * years;// -1474836480 因为先运算再定义为long,运算结果已经是int上限了
        long total3 = money * (long)years;// 20000000000
        long total4 = (long)money * years;// 20000000000
        System.out.println(total1);
        System.out.println(total2);
        System.out.println(total3);
        System.out.println(total4);
    }
}

变量、常量、作用域

Java是强类型语言,每个变量都必须声明其类型

Java变量是程序中最基本的存储单元,要素包括==变量名、变量类型和作用域==

变量

type varName [=value] [{,varName=[value]}];
//数据类型 变量=值 (可以用逗号隔开来声明多个同类型变量,注意跟python的格式区别)

注意:

  1. 每个变量都必须说明类型,可以是基本类型也可以是引用类型
  2. 不建议一行定义多个变量,影响程序可读性
  3. 可以只定义变量名和数据类型,不定义值(局部变量例外)

变量作用域

  • 类变量:独立于方法之外的变量,用 static 修饰
  • 实例变量:独立于方法之外的变量,不过没有 static 修饰
  • 局部变量:类的方法中的变量
变量 定义位置 初始化值 生命周期 作用范围 内存位置
类变量 类中方法外以static关键字声明 有默认的初始化的值 第一次访问时创建,在程序结束时销毁 整个类中有效 静态存储区
实例变量 类中方法外 有默认的初始化的值 对象的存在而存在,随着对象的消失而消失 整个类中有效 堆内存
局部变量 方法内 不赋值不能用 方法的调用而存在,方法调用完毕而消失 所在方法有用 栈内存
public class Variable{
    // 在类中方法之外的是属性(一种特定变量)
    static int allClicks=0;     // 类变量
    String str="hello world";   // 实例变量
    
    public void method(){
        int i=0;                // 局部变量
    }
}
public class Demo08 {
    // 类变量 static
    static double salary = 25_000;

    // 实例变量:从属于对象;如果不自行初始化,这类型的默认值为 整数(0) 浮点数(0.0) 字符(''=无) 布尔值(False)
    // 除了基本类型,其余默认都是null(包括String)
    String name;
    int age;

    // main方法
    public static void main(String[] args) {
        // 局部变量:必须声明和初始化值
        int i =10;
        System.out.println(i);

        // 变量类型可以是自己定义的类
        // 变量类型 变量名字 = new Demo08(); 只有变量名字可以随意命名
        Demo08 demo08 = new Demo08();
        System.out.println(demo08.age);
        System.out.println(demo08.name);
        // 类变量 static
        System.out.println(salary);   // 注意类变量不用new一个实例出来再调用
    }

    // 其他方法
    public void add(){
    //    System.out.println(i);
    //    此处报错,因为i的作用域只在定义的main方法内
    }
}

还有一种静态代码块也可以定义变量,以后再说

以上在面向对象中会再详细说

常量

常量(constant):初始化(initialize)后不能再改变值,是不会变动的值

所谓常量可以理解成一种特殊的变量,它的值被设定之后,在程序运行过程中不允许被改变

关键字(修饰符):final

// final 常量名 = 值;
final double PI = 3.14;

常量名一般使用全大写字符

public class Demo09 {

    // 修饰符不存在先后顺序 static和final先后顺序无所谓(包括public和private)
    static final double PI = 3.14;

    public static void main(String[] args) {
        System.out.println(PI);
    }
}

变量的命名规范

  • 所有变量、方法、类名:见名知意
  • 类成员变量:首字母小写和驼峰原则:monthSalary
  • 局部变量:首字母小写和驼峰原则:lastName
  • 常量:大写字母和下划线:MAX_VALUE
  • 类名:首字母大写和驼峰原则:GoodMan
  • 方法名:首字母小写和驼峰原则:runFirst()

基本运算符

Java语言支持如下运算符

  • 算术运算符:+,-,*,/,%,++,--
  • 赋值运算符:=
  • 关系运算符:>,<,>=,<=,==,!=,instanceof
  • 逻辑运算符:&&,||,!
  • 位运算符:&,|,^,~,>>,<<,>>>(了解为主)
  • 条件运算符:?:
  • 扩展赋值运算符:+=,-=,*=,/=

扩展:在src下新建package等同于新建子文件夹用于分类整理

image-20210610205156100

注意:不同package中的Java文件名和类名可以重复,但不能删除第一句声明package的语句

image-20210610205438794

算术运算符

package operator;

public class Demo01 {
    public static void main(String[] args){
    //    二元运算符 + - * / %
        int a = 10;
        int b = 20;
        int c = 25;
        int d = 25;
        System.out.println(a+b);
        System.out.println(a-b);
        System.out.println(a*b);
        System.out.println(a/(double)b);
    }
}
/*
* 30
* -10
* 200
* 0.5
* */
// 如果直接 a/b 结果会是0 因为int运算是舍去小数部分的
package operator;

public class Demo02 {
    public static void main(String[] args) {
        long a = 1231231232123123L;
        int b = 123;
        short c = 10;
        byte d = 8;

        System.out.println(a + b + c + d);  // long
        System.out.println(b + c + d);  // int
        System.out.println(c + d);  // int
        // System.out.println((String)(c + d));  // Inconvertible types; cannot cast 'int' to 'java.lang.String'
        // 算数运算中只要含任何一个long类型变量 结果就是long类型
        // 只要不含long类型 整数的算数运算结果一定是int类型 无论有没有int类型的运算变量
        float e = 0.5F;
        double f = 2.5;
        float g = 1.5F;

        System.out.println(e+f);
        System.out.println(e+g);
        // System.out.println((String)(e+g);  // Inconvertible types; cannot cast 'double' to 'java.lang.String'
        // 同理 算数运算中只要含任何一个double类型变量 结果就是double类型
        // 但是 不同的是 如果运算只含float类型变量 结果仍然为float类型
    }
}

  • 整数运算
    • 运算中只要含任何一个long类型变量,结果就是long类型
    • 只要不含long类型,运算结果一定是int类型,无论有没有int类型的运算变量
  • 浮点数运算
    • 运算中只要含任何一个double类型变量,结果就是double类型
    • 只要不含double类型,运算结果一定是float类型
  • 整数和浮点数运算之后,结果是浮点数

关系运算符

package operator;

public class Demmo03 {
    public static void main(String[] args) {
        // 关系运算符返回结果是Boolean值
        int a = 10;
        int b = 20;
        int c = 21;

        System.out.println(c%a);// %称为模运算符 是一种算术运算符
        // 注意跟数论里面一般的模运算不太一样 余数可以小于0 除数和被除数也都可以小于0

        System.out.println(a>b);
        System.out.println(a<b);
        System.out.println(a==b);
        System.out.println(a!=b);
    }
}
/*
* false
* true
* false
* true
* */


package operator;

class Bird{//父类

}
class BigBird extends Bird{//Bird的子类

}
class LittleBird extends Bird implements BirdFly{//Bird的子类加上BirdFly接口

}
interface BirdFly{

}
public class JavaKWinstanceof {
    public static void main(String[] args) {
        // TODO Auto-generated method stub
        JavaKWinstanceof myThis=new JavaKWinstanceof();
        Bird myBird=new Bird();
        BigBird myBigBird=new BigBird();
        LittleBird myLittleBird=new LittleBird();
        //打印各实例与类之间的关系
        System.out.println(myThis instanceof JavaKWinstanceof);;//打印结果:true
        System.out.println(myBird instanceof Bird);//打印结果:true
        System.out.println(myBigBird instanceof Bird);//打印结果:true     子类实例出的对象也是父类的实例
        System.out.println(myBird instanceof BigBird);//打印结果:false      父类实例出的对象不是子类的实例
        System.out.println(myLittleBird instanceof Bird);//打印结果:true  子类加上其它接口后实例出的对象也是父类的实例

    }
}

自增自减运算符、初识Math类

package operator;

public class Demo04 {
    public static void main(String[] args) {

    //    一元运算符  ++  --  自增  自减
        int a = 3;
        int b = a++;  // 先执行完等号右边的代码,给b赋值,再自增
        // 等价于int b = a; a = a + 1;
        System.out.println(a);
        // 等价于a = a + 1; int c = a;
        int c = ++a;  // 先自增,执行完等号右边的代码,再给b赋值

        System.out.println(a);
        System.out.println(b);
        System.out.println(c);


    //    很多运算会使用一些工具类操作 例如数学运算会经常使用Math类
    //    幂运算  2^3 = 2**3 = 2*2*2 = 8
        double pow = Math.pow(2, 3);
        System.out.println(pow);


    }
}
/*
* 4
* 5
* 3
* 5
* 8.0
* */

逻辑运算符、位运算符

注意:

  • 逻辑与运算如果第一个变量是false,则结果一定是false,因此不会运行第二个变量
  • 逻辑或运算如果第一个变量是true,则结果一定是true,因此不会运行第二个变量

称为短路运算

package operator;

// 逻辑运算符
public class Demo05 {
    public static void main(String[] args) {
    //    与 (and)  或 (or)  非 (取反)
        boolean a = true;
        boolean b = false;

        System.out.println("a && b : " + (a && b));  // 逻辑与运算:两个变量都为真结果才为true
        System.out.println("a || b : " + (a || b));  // 逻辑或运算:两个变量有一个为真,则结果才会true
        System.out.println("!(a && b) : " + !(a && b));  // 逻辑非运算:假if真,真if假

    //    短路运算
        int c = 5;
        boolean d = (c < 4) && (c++ < 4);
        System.out.println(d);  // 与运算的第一个变量已经是false了,结果一定是false
        System.out.println(c);  // c仍然为5说明根本没执行 && 后面的内容

        boolean e = (c > 4) || (c++ < 4);
        System.out.println(e);  // 或运算的第一个变量已经是true了,结果一定是true
        System.out.println(c);  // c仍然为5说明根本没执行 || 后面的内容
    }
}
/*
* a && b : false
* a || b : true
* !(a && b) : true
* false
* 5
* true
* 5
* */

(面试常考)

提问:2 * 8 = 16怎么运算最快

答:用2<<3代替2*8,因为位运算最快,效率最高

注意:以下位运算实际都是对补码做的,只是除了位取反之外,其他位运算对原码和补码计算结果都一样

package operator;

// 位运算符:跟二进制有关
public class Demo06 {
    public static void main(String[] args) {
    //   按位与&  按位或|  按位非~  按位异或^  按位取反~(连带符号位一起取反)
        /*
        * A = 0011 1100
        * B = 0000 1101
        *
        * A&B = 0000 1100
        * A|B = 0011 1101
        * A^B = 0011 0001 (异或是二者不一样为1,一样为0)
        * ~B = 1111 0010 
        * */
        int A = 0b00111100;
        int B = 0b00001101;
        String str1 = Integer.toBinaryString(A & B);
        String str2 = Integer.toBinaryString(A | B);
        String str3 = Integer.toBinaryString(A ^ B);
        String str4 = Integer.toBinaryString(~B);
        int i1 = Integer.parseInt(str1);
        int i2 = Integer.parseInt(str2);
        int i3 = Integer.parseInt(str3);
        // 这里因为 ~B 是负数-14,int类型占内存空间32位按补码形式存储(负数的补码前面有很多1),若按字符转为int会超出int的上限,所以直接截取后8位即可
        String r1 = String.format("%08d", i1);
        String r2 = String.format("%08d", i2);
        String r3 = String.format("%08d", i3);
        String r4 = str4.substring(str4.length()-8, str4.length());
        System.out.println("A & B : "+(A&B)+" = "+r1);
        System.out.println("A | B : "+(A|B)+" = "+r2);
        System.out.println("A ^ B : "+(A^B)+" = "+r3);
        System.out.println("~B : "+(~B)+" = "+r4);
        // A & B : 12 = 00001100
        // A | B : 61 = 00111101
        // A ^ B : 49 = 00110001
        // ~B : -14 = 11110010


    //   提问:2 * 8 = 16怎么运算最快  答:用2<<3代替2*8,因为位运算最快,效率最高
    //   左移<<  有符号右移>>  无符号右移>>>
        /*
        * 0000 0000    0
        * 0000 0001    1
        * 0000 0010    2
        * 0000 0011    3
        * 0000 0100    4
        * 0000 1000    8
        * 0001 0000    16
        * a<<b就是把a的二进制数字往左移动b位,等同于a * Math.pow(2,b),右侧补000…
        * a>>b就是把a的二进制数字往右移动b位,等同于a / Math.pow(2,b),右侧舍去,左侧用符号位补所有空缺的位置
        * >>>是无符号右移  >>是有符号右移 没有无符号左移
        * 有符号右移>>就是右移之后,左边的补上符号位,正数补000…,负数补111…
        * 无符号右移>>>就是右移之后,无论该数为正还是为负,右移之后左边都是补上000…
        * 左移<<不区分有符号和无符号,都是左移之后右边补上0,最左边的符号位也直接移走,所以大数字左移有可能会导致符号变化
        * a>>>b无法写成等价的数学运算形式,但是对于a>0来说,与a>>b是一样的
        * */

        System.out.println("java 右移");
        int a = -5;
        System.out.println(Integer.toBinaryString(a));
        System.out.println(Integer.toBinaryString(a >> 2)); // 有符号右移
        System.out.println(Integer.toBinaryString(a >>> 2)); // 无符号右移

        //        java 右移
        //        11111111111111111111111111111011
        //        11111111111111111111111111111110
        //        111111111111111111111111111110      注:前边的两个0被省略了


        System.out.println("java 负数 左移");
        int b = Integer.MIN_VALUE;
        System.out.println(Integer.toBinaryString(b));
        System.out.println(Integer.toBinaryString(b << 2));
        //      java 负数 左移
        //      10000000000000000000000000000000   // 前面说过,用1000……0000来表示最小值的目的是为了区分+0和-0
        //      0                                  注:不保留符号位,符号位直接移走

        System.out.println("java 正数 左移");
        int c = 5;
        System.out.println(Integer.toBinaryString(c));
        System.out.println(Integer.toBinaryString(c << 2));
        //      java 正数 左移
        //      101
        //      10100

    }
}

手动计算位取反结果方法:

​ 先将数字化为二进制补全应有的字节(int是32位),根据数字的原码计算补码,然后将补码各位置取反,再根据取反后的补码计算原码,将原码化作十进制即可

例如:上例中B=13,即0000 1101,补码等于本身,~0000 1101 = 1111 0010,1111 0010(补码)=1111 0001(反码)=1000 1110(原码)= -14,即~B = -14,计算过程实际是32位但是这楼里省略了中间的0000……和1111……结果是一样的

三元运算符及小结

以下运算符都是用来偷懒的

扩展赋值运算符字符串连接符

package operator;

public class Demo07 {
    public static void main(String[] args) {
        int a =10;
        int b =20;

        a+=b; // a = a+b
        System.out.println(a);
        // 30

        a-=b; // a = a-b
        System.out.println(a);
        // 10

        // 字符串连接符 + 只要两侧有一个变量是String类型,就会把另一侧的变量也自动转换为String,再进行连接
        System.out.println(a+b);
        // 30
        System.out.println(""+a+b);// 这里""先和a进行连接,变成"10",再和20进行连接
        // 1020
        System.out.println(a+b+"");// 这里a+b先进行计算,得到30,再和""进行字符连接
        // 30
    }
}

三元运算符(条件运算符)

package operator;

public class Demo08 {
    public static void main(String[] args) {
    //    x ? y : z
    //    如果x==true,则结果为y,否则结果为z

        int score = 80;
        String type = score < 60 ? "不及格" : "及格";
        // 等同于if语句
        System.out.println(type);
        // 及格
    }
}

运算符优先级

运算符大致优先级:括号,一元运算符,一般二元运算符,三元运算符,赋值运算符与扩展赋值运算符

结合性:同等优先级情况下,在代码中的计算顺序(一般是从左向右计算,但有例外)

详细如下:(优先级2中的+和-不是算数运算符而是一元运算符,表示正负号)

优先级 运算符 结合性
1 ()、[]、{} 从左向右
2 !、+、-、~、++、-- 从右向左
3 *、/、% 从左向右
4 +、- 从左向右
5 «、»、>>> 从左向右
6 <、<=、>、>=、instanceof 从左向右
7 ==、!= 从左向右
8 & 从左向右
9 ^ 从左向右
10 | 从左向右
11 && 从左向右
12 || 从左向右
13 ?: 从右向左
14 =、+=、-=、*=、/=、&=、|=、^=、~=、«=、»=、>>>= 从右向左

注意:多数运算符具有左结合性,一元运算符、三元运算符、赋值运算符具有右结合性

package operator;

public class RightAssociation {
    public static void main(String[] args) {
        // 结合性问题
        // 三目运算	?:
        int a = 3;
        int b = 2;
        int c = 1;
        int result = a > b ? a : b > c ? b : c;
        //如果是左结合:
        // a > b ? a : b 先计算,得到结果是a
        // a > c ? b : c 后计算,得到结果是b
        // result = b = 2

        // 如果是右结合:
        // b > c ? b : c 先计算,得到结果是b
        // a > b ? a : b 后计算,得到结果是a
        // result = a =3

        System.out.println("result=" + result);//结果是3,证明三目运算符是右结合的,即从右向左运算


        //单目运算符 ++	--	~	!	(type)	sizeof

        int single = 2;
        int re = ~ single ++;
        //左结合的话:
        // ~ single 先计算,得到结果是-3;single=2 (计算取反结果:~00…0010 -> 11…1101 -> 11…1100 -> 10…0011 = 等于-3)
        // re = -3++后计算,得到结果是re=-3;single=3;

        //右结合的话:
        //temp=single;single=single+1;	其结果temp=2;single=3
        //re = ~ temp;~00000010(正数补码等于原码)得到11111101(补码),11111100(反码),10000011(原码),即 re = -3
        System.out.println("re=" + re);//re =-3
        System.out.println("single=" + single);//single=-3
        
        /*
        * 计算错了
        * 以上举例不能体现单目运算符的右结合性,但是对于理解"后加加"赋值的过程很有帮助
        * */
    }
}

包机制

为了更好地组织类,Java提供了包机制,用于区别类名的命名空间

包(package)的本质就是文件夹

包语句的语法格式为:

package pkg1[.pkg2[.pkg3…]];
//  其中.是区分包的上下级路径分隔符

规范:一般利用公司域名倒置座位包名

例如www.baidu.com是公司域名,那包名就为com.baidu.www

如果无法创建多级空包,则在 project面板 > show options menu齿轮 > compact middle packages 取消勾选

image-20210612000122987

正确建立com.baidu.www的包,应该如下

image-20210612000456738

同理可以建立com.baidu.wenku或者com.baidu.zhidao等等其他包,等效于在baidu下面新建子包

然后按照规范重新格式化之前的包名如下

image-20210612000820218

其中的java类在idea中经过refractor,会在最上方自动补充一句package com.kimtanyo.operator;(但是要刷题要记得手动加)image-20210612001001677

为了能够使用某一个包的成员,我们需要在Java程序中明确导入该包,使用import语句可以完成此功能

import package1[.package2…].(classname|*)

例如:

微信截图_20210626152256

也可以导入自定义包中的java文件,但是注意导入的java文件所定义的类名不可以和当前java文件的类名重复

image-20210626152824427

另外如果想导入某个包下所有的成员,可以使用通配符*

image-20210626154328569

注意:如果导入的包中有类名和当前java文件类名重复,此时不会报错,但是在引用的使用会优先使用当前java文件的类

课后:看阿里巴巴Java开发手册

JavaDoc生成文档

JavaDoc命令是用来生成自己API文档的

参数信息

  • @author 作者名
  • @version 版本号
  • @since 指明最早需要使用的jdk版本
  • @param 参数名
  • @return 返回值情况
  • @throws 异常抛出情况

jdk8 API文档:官方地址中文翻译

文档注释

文档注释分为类注释和方法注释,注意类注释写在类定义前面,方法注释写在方法定义前面,中间不要隔其他内容

快捷键是输入/**,然后回车,尤其是方法注释会自动补全内容

package com.kimtanyo.base;

/**
 * @author kimtanyo
 * @version 1.0
 * @since 1.8
 */
public class Doc {
    String name;

    /**
     *
     * @param name
     * @return
     * @throws Exception
     */

    public String test(String name)throws Exception{
        return name;
    }
}

image-20210626160520494

javadoc命令行

打开java文件所在文件夹

image-20210626161313958

在文件夹导航栏输入cmd,在当前java文件的路径打开命令行,输入javadoc命令,生成指定java文件的文档,但是如果java文件中有中文,防止出现乱码,因此要用UTF-8编码且让字符集也为UTF-8

# 格式:javadoc 参数 java文件名
javadoc -encoding UTF-8 -charset UTF-8 Doc.java
image-20210626162213896
image-20210626162243547

生成成功以后打开目录索引页index.html,会得到一个跟官方API文档极其类似的文档,打开index中的Doc类,会得到一些说明

image-20210626162414833

IDEA生成JavaDoc文档

打开Tools -> Generate JavaDoc,选中java文件、输出路径和参数即可生成

image-20210626163304748 image-20210626163211337