初始计算机和Java语言
Java语言的概述
Java诞生史
20世纪90年代,出现了单片机系统,可以用在家电上,Sun公司看中了这个商机,开展了绿色计划项目。
项目负责人是詹姆斯-高斯林,他打算用C++控制单片机,但是他发现C++无法跨平台,单片机换了,所有代码都得重写,于是打算在C++基础上实现一种新语言来允许跨平台,就是后来的Java。
开发环境的搭建和使用
JDK的目录结构
- bin:命令
- conf:配置相关
- include:头文件
- jmods:JDK各种模块
- legal:JDK各模块的授权文档
- lib:补充jar包和源代码
- javac.exe:编译器,将源代码编译成字节码文件
- java.exe:解释器,将启动JVM对字节码文件进行解释并执行
Java11新特性:简化编译。java HelloWorld直接帮我们先编译后执行,前提是class字节码文件不存在。
多行注释不允许嵌套使用。
跨平台原理
通过JVM。
变量和数据类型
当需要在程序中记录单个数据内容时,则声明一个变量即可,而声明变量的本质就是在内存中申请一个存储单元,由于该存储单元中的数据内容可以发生改变,因此得名为变量。
数据类型的分类
①基本数据类型:byte, short, int, long, float, double, boolean, char
②引用数据类型:数组、类、接口、枚举、标注
进制
日常生活:逢十进一。权重(拳重)
千百十个
1 2 3 4
计算机底层:逢二进一。
正十进制 -> 二进制
①除2取余,直到商为0,将余数逆序排序。(0b/0B可以作为二进制的前缀)
②拆分法,45 = 32 + 8 + 4 + 1 = 101101
- 最高位代表符号位,0为正数,1为负数
- ∴ 64位机器最大能表示2^63 - 1,即long的最大值。即2^63 - 1。
- 01111111 11111111 11111111 11111111 11111111 11111111 11111111 11111111
负十进制 -> 二进制
①绝对值转换为二进制
②按位取反 + 1
高位全是1
负二进制 -> 十进制
①减1按位取反
②添加负号
单个字节表示的整数范围
非负数:0000 0000 ~ 0111 1111,即 2^7 - 1
负数:1000 0000 ~ 1111 1111,减1取反加负号,即为2^7
整数类型
①byte(1)、short(2)、int(4)、long(8)
②byte:-2^7 ~ 2^7 - 1、short:-2^15 ~ 2^15、int:-2^31 ~2^31-1、long:-2^63 ~2^63-1
③int a = 100,100指的是直接量或常量,默认是int类型。
④int a = 2200000000,这个会报错并不是a容纳不下,而是直接量本身是int类型,容纳不下这么大数。
⑤为什么变量名不能数字开头?会和直接量冲突。
⑥int a=25;byte b=a?错误。a是变量,数值随时可能发生改变,编译期间编译器并不能判断a的值会不会变,所以无法保证将int类型的a赋值给byte类型的b不会出问题,所以直接编译报错。
⑦强制类型转换:short s = 128; byte b = (byte)s; short 占两字节,byte占一字节,0000 0000 1000 0000 => 截断成 1000 0000 => 最高位代表负号 => 减一取反加负号 => -128。
浮点数
①float:占4个字节,7位有效数字,单精度。
②double:占8个字节,15位有效数字,双精度,d = 1000.0,小数数据也是直接量,默认是double类型。
字符类型
①char:2个字节,表示的范围即为65535。
②ASCII:字母和整数的对照表。’0‘ - 48、’A‘ - 65、’a‘ - 97、空格 - 32、\n - 10。
③Unicode:对世界字符进行编号,每个字符用16位表示。奇 -> \u5947。
运算符
①尝试判断一下结果的输出
System.out.println(5 / 2); // 2 System.out.println(5 / (double)2); // 2.5 System.out.println((double)5 / 2); // 2.5 System.out.println(5 * 1.0 / 2); // 2.5 推荐 System.out.println(5.0 / 2); // 2.5 System.out.println(5 / 0); // java.lang.ArithmeticException: / by zero (算数异常) System.out.println(5 / 0.0); // Infinity: 无穷大 System.out.println(0 / 0.0); // NaN②编程使用算数运算符实现秒数的拆分
public class ShowHourTime { public static void transfer(int num) { // 3666 => 3600 + 60 + 6 int hour = num / 3600; int min = num % 3600 / 60; // int sec = num % 3600 % 60; // 正常思维 int sec = num % 60; System.out.println(num + "秒转换结果:" + hour + "时" + min + "分" + sec + "秒"); // 判断如下输出结果 System.out.println(hour + min + sec); // 8 System.out.println(hour + "" + min + sec); // 116 System.out.println(hour + min + "" + sec); // 26 System.out.println(hour + min + sec + ""); // 8 } public static void main(String[] args) { // 提示用户输入秒数 Scanner scanner = new Scanner(System.in); System.out.print("请输入秒数:"); int num = scanner.nextInt(); ShowHourTime.transfer(num); } }③尝试判断一下结果的输出
// a++是表达式,a是变量,所占用的内存空间不同 int a = 10; a++; ++a; a--; --a; System.out.println(a++); // 10 System.out.println("a=" + a); // 11 System.out.println(++a); // 12 System.out.println("a=" + a); // 12 System.out.println(a++ + ++a); // 从左到右计算, a++的值为12, a的值为13, ++a的值为14, a的值为14, 之和为26④尝试判断一下结果的输出
// 短路特性: &&前面为假,后面不执行; ||前面为真,后面不执行 int a = 3; int b = 5; boolean flag = (++a == 3 && ++b == 5); System.out.println("flag=" + flag); // false System.out.println("a=" + a); // 4 System.out.println("b=" + b); // 5 flag = (++a == 5 || ++b == 5); System.out.println("flag=" + flag); // true System.out.println("a=" + a); // 5 System.out.println("b=" + b); // 5⑤提示用户输入一个正整数,使用逻辑运算符判断该正整数是否为三位数,若是则打印true,否则打印false。
public static void isThreeBit(int num) { // ①判断该整数是否为三位数 // ②打印 System.out.println(num >= 100 && num <= 999); System.out.println((num >= 100 && num <= 999) ? true: false); }⑥尝试判断一下结果的输出
int a = 5; int b = 9; int c = 10; int d = 11; System.out.println(b = a = c = d); // 11, 从右往左赋值⑦尝试判断一下结果的输出
// 赋值运算 byte b2 = 20; // b2 = b2 + 1; // 错误:不兼容的类型,从int转换到byte可能会有损失 byte + int相加结果还是int类型 // b2 = b2 + (byte)1; // 错误:不兼容的类型,从int转换到byte可能会有损失 byte + byte相加结果还是int类型,这是编译器防止溢出所做的优化 b2 = (byte) (b2 + 1); // OK b2 += 1; // OK, 等价于 b2 = (byte) (b2 + 1)⑧尝试判断一下结果的输出
// << 左移:右边用0补充 // >> 右移:左边用1补充 // >>> 逻辑右移:左边用0补充 byte b3 = -127; // 0111 1111 => 1000 0000 => 1000 0001 System.out.println(b3 >> 2); // 1000 0001 => 1110 0000 => 1101 1111 => 1010 0000 => -32 System.out.println(b3 << 2); // 会将b3提升为int型,1111 1111 1000 0001 => 1111 1110 0000 0100 => 1111 1110 0000 0011 => 1000 0001 1111 1100 => -(2^9-4) System.out.println(b3 >>> 2); // 会将b3提升为int型,1111 1111 1000 0001 => 0011 1111 1110 0000 直接变成正数了⑨尝试判断一下结果的输出
// 位运算 byte b4 = 11; byte b5 = 13; System.out.println(b4 & b5); // 0000 1011 & 0000 1101 => 0000 1001 => 9 System.out.println(b4 | b5); // 0000 1011 | 0000 1101 => 0000 1111 => 15 System.out.println(b4 ^ b5); // 0000 1011 | 0000 1101 => 0000 0110 => 6 System.out.println(~b4); // 0000 1011 => 1111 0100 => 1111 0011 => 1000 1100 => -12
流程控制
①输入公里数和等候秒数,输出车费。
出租车计费方式:由里程数和等候时间钱数相加得出。里程数前三公里13元,超过3公里到15公里每公里2元,15公里以上每公里3元,等候时间每2分半1元,不足不收钱。
// 出租车计费方式:由里程数和等候时间钱数相加得出。 // 里程数前三公里13元 // 超过3公里到15公里每公里2元 // 15公里以上每公里3元 // 等候时间每2分半1元,不足不收钱 public static double pay(double mile, int waitTime) { // ①先算里程数需要支付的钱 double milePay = 0; if (mile <= 3) { milePay = 13; } else if (mile <= 15) { milePay = (mile - 3) * 2 + 13; } else { milePay = (mile - 15) * 3 + (15 - 12) * 2 + 13; } // ②再算等待时间需要支付的钱 // ③最后支付总价为二者之和 milePay += waitTime / 150; return milePay; }②判断考试成绩所在的位置。
public static void judge(int score) { char grade; switch (score / 10) {/*支持byte/short/int/char/String/enum*/ case(10): /*case穿透,匹配10如果没有break,会执行下一个break之前的所有代码*/ case(9): grade = 'A';break; case(8): grade = 'B';break; case(7): grade = 'C';break; case(6): grade = 'D';break; default: grade = 'E'; } System.out.println("等级为" + grade); }③打印1-100之间的奇数,用三种方式。
// 方式一:除2除不尽 for (int i = 1; i < 100; i++) { if (i % 2 != 0) { System.out.print(i + " "); } } System.out.println(); // 方式二:等差 for (int i = 1; i < 100; i = i + 2) { System.out.print(i + " "); } System.out.println(); // 方式三:2*i-1 for (int i = 1; i <= 50; i++) { System.out.print(2 * i - 1 + " "); }④打印三位数中的所有水仙花数
// 水仙花数:一个整数满足其值等于各个数位的立方和。如153 = 1^3 + 5^3 + 3^3。 public static void show() { for (int num = 100; num <= 999; num++) { // ①取出整数的百分位、十分位、个分位 int b = num / 100; int s = num % 100 / 10; int g = num % 10; // ②将每位立方并相加,判断和num是否相等 if (num == (b * b * b + s * s * s + g * g * g)) { // ③相等则打印 System.out.print(num + " "); } } }⑤打印九九乘法表
// ①打印左直角三角形 // i: 行,j:列 // 极端:i = j for (int i = 1; i <= 5; i++) { for (int j = 1; j <= i; j++) { System.out.print("*"); } System.out.println(); } // ②打印右直角三角形 // i: 行,j:列 // 极端:i + j = 6 for (int i = 1; i <= 5; i++) { for (int j = 1; j + i <= 6; j++) { System.out.print("*"); } System.out.println(); } // ③打印等边三角 // i: 行,j:列 // 极端:j = 2*i - 1 for (int i = 1; i <= 5; i++) { for (int j = 1; j + i <= 6; j++) { System.out.print(" "); } for (int j = 1; j <= 2*i - 1; j++) { System.out.print("*"); } System.out.println(); } // ④九九乘法表 // i: 行,j:列 // 等价于左直角三角形 for (int i=1; i<=9; i++) { for (int j=1; j<=i; j++) { System.out.print(j + "*" + i + "=" + i*j + " "); } System.out.println(); }⑥打印1-100的素数
boolean flag; for (int i=2; i<=100; i++) { flag = true; // ①优化点:没必要一直除,只需要除到当前数的平方根位置即可,因为随着除数的增大,商必然减小,会造成重复的判断 for (int j=2; j < Math.sqrt(i)/*优化点*/; j++) { if (i % j == 0) { flag = false; break; } } if (flag) { System.out.print(i + " "); } }⑦输入一个正整数,反向输出
public static void reverse(int num) { // ①思路:将整数从右到左按位取出来 // 123 % 10 = 3 123 / 10 = 12 // 12 % 10 = 2 12 / 10 = 1 // 1 % 10 = 1 1 / 10 = 0 int result = 0; while (num > 0) { int bit = num % 10; num = num / 10; result = result * 10 + bit; // 每次乘10变成高位 } System.out.println(result); }⑧检查学习的进度
// do while 适合用于至少考核一次的场景 Scanner scanner = new Scanner(System.in); String s; do { System.out.println("学习中..."); System.out.println("是否及格?[Y/n]"); s = scanner.nextLine(); } while (!"y".equals(s));
数组
①byte、short、int、long以及 char的初始值都是0。
②程序运行时,局部变量存放在栈区,对象和数组存在堆区。(基本类型和引用类型都是在栈中声明,区别在于基本类型保存的是值,而引用类型保存的地址,所以直接打印arr1会打印出一个地址)
③数组的插入和删除
int[] arr = {1, 2, 3, 4, 0}; // 插队:第一次接触边界问题,如何保证不越界? // ①首先从最后开始挪,即从arr.length - 1处开始挪,如果从前挪,就会挤到别人 // ②其次i-1要确保大于0,否则arr[i - 1]就越界了 for (int i = arr.length - 1; i - 1 >= 0; i--) { arr[i] = arr[i - 1]; } // 插进来了 arr[0] = 0; for (int i : arr) { System.out.print(i + " "); // [0,1,2,3,4] } System.out.println(); // 排队 // ①常识里都是前面的人开始往前挪,才会带动后面的人挪 // ②其次保证不越界的条件是i>=n且i+1<arr.length,这样既不会挪过头了,也保证i+1有人,n表示走的那个人的位置,假设1走了 for (int i = 1; i + 1 < arr.length; i++) { arr[i] = arr[i + 1]; } for (int i : arr) { System.out.print(i + " "); // [0,2,3,4,4] } System.out.println(); // 拷贝:队伍保持不变,换个新地方排队 int[] arr2 = new int[arr.length * 2]; for (int i = 0; i < arr.length; i++) { arr2[i] = arr[i]; } System.arraycopy(arr, 0, arr2, 0, arr.length); /*官方native方法*/ for (int i : arr2) { System.out.print(i + " "); // [0,2,3,4,4] }④数组的优缺点
内存是连续的,所以可以通过内存地址偏移量来访问指定位置元素,访问速度很快,时间复杂度为O(1)。
这也是数组的缺点,要求内存空间必须连续,可以使内存产生不能用的碎片。
长度不能改变,如果空间不够用,只能重新开辟更大的空间,将原数组数据依次拷贝过去。
插入和删除效率低,一个人动了全得动一下。
⑤统计正整数中每个数字出现的次数
public static int[] count(int num) { int[] count = new int[10]; while (num > 0) { int bit = num % 10; // 取出最低位 count[bit] ++; num = num / 10; // 去掉最低位 } return count; }⑥Arrays工具类
int[] arr = {6, 2, 3, 4, 5, 1}; int[] arr2 = {1, 2, 3, 4, 5, 6}; // 比较 System.out.println(Arrays.equals(arr, arr2)); // 排序: 从小到大 Arrays.sort(arr); System.out.println(Arrays.toString(arr)); // 填充 Arrays.fill(arr, 0, arr.length, 100); // 二分查找 System.out.println(Arrays.binarySearch(arr, 0, arr.length, 100)); // 打印 System.out.println(Arrays.toString(arr));⑦二维数组
- 由多个一维数组垛在一起构成。每个一维数组代表一行。
- 扫雷界面就是一个二维数组。