IDE相关

vsCode调试Java输出中文乱码——解决方法

1、切换VS Code 的终端为 CMD(vsCode默认使用的PowerShell 有时兼容性差)

打开 VS Code 设置(Ctrl+,),搜索terminal.integrated.defaultProfile.windows

把默认终端从PowerShell改成Command Prompt(CMD);

2. 修改 settings.json(VS Code 终端编码)

Ctrl+, 打开设置,搜索 encoding

找到 Files: Encoding,设置为 UTF-8

搜索 terminal.integrated.defaultProfile.windows,设置为 Command Prompt(CMD)

搜索 terminal.integrated.profiles.windows,添加 CMD 配置:

1
2
3
4
5
6
"terminal.integrated.profiles.windows": {
"Command Prompt": {
"path": "C:\\Windows\\System32\\cmd.exe",
"args": ["/k", "chcp 65001"] // 启动时自动切换UTF-8
}
}

关闭当前终端

再按F5运行代码,乱码必解决。


Java 代码逻辑优化

执行效率

1、开发中,如果既可以使用条件运算符,又可以使用if-else,推荐使用条件运算符。因为执行效率稍高。

  • 条件运算符格式:
1
(条件表达式)? 表达式1:表达式2
  • 说明:条件表达式是boolean类型的结果,根据boolean的值选择表达式1或表达式2

2、逻辑运算符

  • 区分“&”和“&&”:

    • 相同点:如果符号左边是true,则二者都执行符号右边的操作

    • 不同点:& : 如果符号左边是false,则继续执行符号右边的操作

    ​ && :如果符号左边是false,则不再继续执行符号右边的操作

    • 建议:开发中,推荐使用 &&
  • 区分“|”和“||”:

    • 相同点:如果符号左边是false,则二者都执行符号右边的操作

    • 不同点:| : 如果符号左边是true,则继续执行符号右边的操作

      ​ || :如果符号左边是true,则不再继续执行符号右边的操作

    • 建议:开发中,推荐使用 ||

3、if-else语句与switch-case语句比较

  • 结论:凡是使用switch-case的结构都可以转换为if-else结构。反之,不成立。
  • 开发经验:如果既可以使用switch-case,又可以使用if-else,建议使用switch-case。因为效率稍高。
  • 细节对比:
    • if-else语句优势
      • if语句的条件是一个布尔类型值,if条件表达式为true则进入分支,可以用于范围的判断,也可以用于等值的判断,使用范围更广
      • switch语句的条件是一个常量值(byte,short,int,char,枚举,String),只能判断某个变量或表达式的结果是否等于某个常量值,使用场景较狭窄
    • switch语句优势
      • 当条件是判断某个变量或表达式是否等于某个固定的常量值时,使用if和switch都可以,习惯上使用switch更多。因为效率稍高。当条件是区间范围的判断时,只能使用if语句。
      • 使用switch可以利用穿透性,同时执行多个分支,而if…else没有穿透性。

Java 语言规则特别注意的地方

1、boolean类型数据只有两个值:true、false,无其它。

数组知识点总结

数组定义

数组(Array),是多个相同类型数据按一定顺序排列的集合,并使用一个名字命名,并通过编号的方式对这些数据进行统一管理。

数组中的概念

  • 数组名
  • 下标(或索引)
  • 元素
  • 数组的长度

数组的特点

  • 数组本身是引用数据类型,而数组中的元素可以是任何数据类型,包括基本数据类型和引用数据类型。
  • 创建数组对象会在内存中开辟一整块连续的空间。占据的空间的大小,取决于数组的长度和数组中元素的类型。
  • 数组中的元素在内存中是依次紧密排列的,有序的。
  • 数组名中引用的是这块连续空间的首地址。
  • 数组,一旦初始化完成,其长度就是确定的。数组的长度一旦确定,就不能修改。如果想扩容或者是缩容,只能重新建一个数组,把旧数组的值赋给新数组,然后再把新数组的地址复制给旧数组即可。
  • 我们可以直接通过下标(或索引)的方式调用指定位置的元素,速度很快。
  • 数组下标为什么是0开始,因为第一个元素距离数组首地址间隔0个单元格。

一维数组

声明

格式:

1
2
3
4
5
//推荐
元素的数据类型[] 一维数组的名称;

//不推荐
元素的数据类型 一维数组名[];

举例:

1
2
3
4
int[] arr;
int arr1[];
double[] arr2;
String[] arr3; //引用类型变量数组

注意:Java语言中声明数组时不能指定其长度(数组中元素的个数)。 例如: int a[5]; //非法

初始化

静态初始化

  • 如果数组变量的初始化和数组元素的赋值操作同时进行,那就称为静态初始化。

  • 静态初始化,本质是用静态数据(编译时已知)为数组初始化。此时数组的长度由静态数据的个数决定。

  • 一维数组声明和静态初始化格式1:

    1
    2
    3
    4
    5
    6
    数据类型[] 数组名 = new 数据类型[]{元素1,元素2,元素3,...};



    数据类型[] 数组名;
    数组名 = new 数据类型[]{元素1,元素2,元素3,...};
    • new:关键字,创建数组使用的关键字。因为数组本身是引用数据类型,所以要用new创建数组实体。

    • 静态初始化不需要手动表明数组长度,编译器会自动根据大括号{}中提供的元素个数,计算并确定数组的长度,无需开发者手动指定,这也是静态初始化的便捷性体现。如果在静态初始化时手动添加数组长度,属于语法错误,编译器会直接报错,无法通过编译。

动态初始化

数组变量的初始化和数组元素的赋值操作分开进行,即为动态初始化。

动态初始化中,只确定了元素的个数(即数组的长度),而元素值此时只是默认值,还并未真正赋自己期望的值。真正期望的数据需要后续单独一个一个赋值。

格式:

1
2
3
4
5
6
数据类型[] 数组名字 = new 数据类型[长度];



数据类型[] 数组名字;
数组名字 = new 数据类型[长度];
  • [长度]:数组的长度,表示数组容器中可以最多存储多少个元素。

  • 注意:数组有定长特性,长度一旦指定,不可更改。和水杯道理相同,买了一个2升的水杯,总容量就是2升是固定的。

数组元素的默认值

数组是引用类型,当我们使用动态初始化方式创建数组时,元素值只是默认值。

对于基本数据类型而言,默认初始化值各有不同。

数组元素类型 初始默认值
byte 0
short 0
int 0
long 0L
float 0.0F
double 0.0
char 0或\u0000
boolean false
引用类型 null

对于引用数据类型而言,默认初始化值为null(注意与0不同!)


二维数组

声明

1
2
3
4
5
6
7
//推荐
元素的数据类型[][] 二维数组的名称;

//不推荐
元素的数据类型 二维数组名[][];
//不推荐
元素的数据类型[] 二维数组名[];

面试题:

1
2
int[] x, y[];
//x是一维数组,y是二维数组

5.2.2 静态初始化

格式:

1
int[][] arr = new int[][]{{3,8,2},{2,7},{9,0,1,6}};

定义一个名称为arr的二维数组,二维数组中有三个一维数组

  • 每一个一维数组中具体元素也都已初始化
    • 第一个一维数组 arr[0] = {3,8,2};
    • 第二个一维数组 arr[1] = {2,7};
    • 第三个一维数组 arr[2] = {9,0,1,6};
  • 第三个一维数组的长度表示方式:arr[2].length;
  • 注意特殊写法情况:int[] x,y[]; x是一维数组,y是二维数组。

动态初始化

如果二维数组的每一个数据,甚至是每一行的列数,需要后期单独确定,那么就只能使用动态初始化方式了。动态初始化方式分为两种格式:

格式1:规则二维表:每一行的列数是相同的

1
2
3
4
5
6
7
8
9
//(1)确定行数和列数
元素的数据类型[][] 二维数组名 = new 元素的数据类型[m][n];
//其中,m:表示这个二维数组有多少个一维数组。或者说一共二维表有几行
//其中,n:表示每一个一维数组的元素有多少个。或者说每一行共有一个单元格

//此时创建完数组,行数、列数确定,而且元素也都有默认值

//(2)再为元素赋新值
二维数组名[行下标][列下标] = 值;

格式2:不规则:每一行的列数不一样

1
2
3
4
5
6
7
8
9
10
11
12
//(1)先确定总行数
元素的数据类型[][] 二维数组名 = new 元素的数据类型[总行数][];

//此时只是确定了总行数,每一行里面现在是null

//(2)再确定每一行的列数,创建每一行的一维数组
二维数组名[行下标] = new 元素的数据类型[该行的总列数];

//此时已经new完的行的元素就有默认值了,没有new的行还是null

//(3)再为元素赋值
二维数组名[行下标][列下标] = 值;

举例1:

1
int[][] arr = new int[3][2];

举例2:

1
int[][] arr = new int[3][];
  • 二维数组中有3个一维数组。
  • 每个一维数组都是默认初始化值null (注意:区别于格式1)
  • 可以对这个三个一维数组分别进行初始化:arr[0] = new int[3]; arr[1] = new int[1]; arr[2] = new int[2];
  • 注:int[][]arr = new int[][3]; //非法

数组的工具类

java.util.Arrays类即为操作数组的工具类,包含了用来操作数组(比如排序和搜索)的各种方法。 比如:

  • 数组元素拼接
    • static String toString(int[] a) :字符串表示形式由数组的元素列表组成,括在方括号(”[]”)中。相邻元素用字符 “, “(逗号加空格)分隔。形式为:[元素1,元素2,元素3。。。]
    • static String toString(Object[] a) :字符串表示形式由数组的元素列表组成,括在方括号(”[]”)中。相邻元素用字符 “, “(逗号加空格)分隔。元素将自动调用自己从Object继承的toString方法将对象转为字符串进行拼接,如果没有重写,则返回类型@hash值,如果重写则按重写返回的字符串进行拼接。
  • 数组排序
    • static void sort(int[] a) :将a数组按照从小到大进行排序
    • static void sort(int[] a, int fromIndex, int toIndex) :将a数组的[fromIndex, toIndex)部分按照升序排列
    • static void sort(Object[] a) :根据元素的自然顺序对指定对象数组按升序进行排序。
    • static void sort(T[] a, Comparator<? super T> c) :根据指定比较器产生的顺序对指定对象数组进行排序。
  • 数组元素的二分查找
    • static int binarySearch(int[] a, int key) 、static int binarySearch(Object[] a, Object key) :要求数组有序,在数组中查找key是否存在,如果存在返回第一次找到的下标,不存在返回负数。
  • 数组的复制
    • static int[] copyOf(int[] original, int newLength) :根据original原数组复制一个长度为newLength的新数组,并返回新数组
    • static T[] copyOf(T[] original,int newLength):根据original原数组复制一个长度为newLength的新数组,并返回新数组
    • static int[] copyOfRange(int[] original, int from, int to) :复制original原数组的[from,to)构成新数组,并返回新数组
    • static T[] copyOfRange(T[] original,int from,int to):复制original原数组的[from,to)构成新数组,并返回新数组
  • 比较两个数组是否相等
    • static boolean equals(int[] a, int[] a2) :比较两个数组的长度、元素是否完全相同
    • static boolean equals(Object[] a,Object[] a2):比较两个数组的长度、元素是否完全相同
  • 填充数组
    • static void fill(int[] a, int val) :用val值填充整个a数组
    • static void fill(Object[] a,Object val):用val对象填充整个a数组
    • static void fill(int[] a, int fromIndex, int toIndex, int val):将a数组[fromIndex,toIndex)部分填充为val值
    • static void fill(Object[] a, int fromIndex, int toIndex, Object val) :将a数组[fromIndex,toIndex)部分填充为val对象

举例:java.util.Arrays类的sort()方法提供了数组元素排序功能:

1
2
3
4
5
6
7
8
9
10
11
12
import java.util.Arrays;
public class SortTest {
public static void main(String[] args) {
int[] arr = {3, 2, 5, 1, 6};
System.out.println("排序前" + Arrays.toString(arr));

Arrays.sort(arr);

System.out.println("排序后" + Arrays.toString(arr));
}
}


Java虚拟机的内存划分

区域名称 作用
虚拟机栈 用于存储正在执行的每个Java方法的局部变量表等。局部变量表存放了编译期可知长度的各种基本数据类型、对象引用,方法执行完,自动释放。
堆内存 存储对象(包括数组对象),new来创建的,都存储在堆内存。
方法区 存储已被虚拟机加载的类信息、常量、(静态变量)、即时编译器编译后的代码等数据。
本地方法栈 当程序中调用了native的本地方法时,本地方法执行期间的内存区域
程序计数器 程序计数器是CPU中的寄存器,它包含每一个线程下一条要执行的指令的地址

算法

衡量算法的优劣:

  • 时间复杂度:分析关键字的比较次数和记录的移动次数

常见的算法时间复杂度由小到大依次为:

Ο(1)<Ο(log2n)<Ο(n)<Ο(nlog2n)<Ο(n2)<Ο(n3)<…<Ο(2n)<Ο(n!)<O(nn)

注意,经常将以2为底n的对数简写成logn。

O(1) < O(logn) < O(n) < O(nlogn) < O(n^2) < O(n^3) < O(2^n) < O(n!) < O(n^n)

  • 空间复杂度:分析排序算法中需要多少辅助内存

    1
    一个算法的空间复杂度S(n)定义为该算法所耗费的存储空间,它也是问题规模n的函数。
  • 稳定性:若两个记录A和B的关键字值相等,但排序后A、B的先后次序保持不变,则称这种排序算法是稳定的。

需要掌握的算法

冒泡排序

快速排序(重要)