一、IDEA项目结构:
1.层级关系:
project(工程)——module(模块)——package(包)——class(类)
一个project中可以创建多个module;
一个module中可以创建多个package;
一个package中可以创建多个class
2.Project和Module概念:
在IDEA中Project是最顶级的结构单元,然后就是Module;目前主流的大型项目结构基本都是多Module的结构,这类项目一般是按功能划分的。模块之间彼此可以相互依赖,有着不可分割的业务关系。因此对于一个Project来说:
(1)当为单Module项目的时候,这个单独的Module实际上就是一个Project;
(2)当为多Module项目的时候,多个模块处于同一个Project之中,此时彼此之间具有互相依赖的关联关系;
(3)当然多个模块没有建立依赖关系的话,也可以作为单独一个小项目运行;
3.Module和Package:
在一个Module中,可以有多个Package,Package的命名规则如下:
(1)不含有中文
(2)不要以数字开头
(3)给包取名时一般都是公司域名倒着写,并且都是小写
二、数组的概述
1.需要数组的原因:
将多个数据存储到一起,每个数据称为容器的元素
2.数组的概念:
数组是多个相同类型的数据按照一定顺序排列的集合,并使用一个名字命名,并通过编号的方式对这些数据进行统一管理;
3.数组的特点:
(1)数组本身是引用数据类型,而数组中的元素可以是任何数据类型,包括基本数据类型和引用数据类型
(2)创建数组对象会在内存中开辟一整块的连续空间,占据的空间大小取决于数组的长度和数组中元素的类型
(3)数组中元素在内存中是依次紧密排列的,有序的
(4)数组一旦初始化完成后其长度就是确定的,数组的长度一旦确定就不能修改
(5)可以通过下标或索引的方式调用指定位置的元素
(6)数组名中引用的是这块连续空间的首地址
三、一维数组:
1.一维数组的声明与初始化
静态初始化:数组变量和数组元素同时赋值,此时不需要确定数组的长度
数组类型[] 数组名 = new 数组类型[]{数组元素1,数组元素2,...,数组元素n};
数组类型[] 数组名 = {数组元素1,数组元素2,...,数组元素n};
动态初始化:数组变量和数组元素的赋值分开进行,此时要确定数组的长度
数组类型[] 数组名 = new 数组类型[数组长度];
2.数组元素的调用:
通过脚标的方式获取数组的元素,脚标的范围从0开始,到数组长度-1结束
数组名[脚标];
3.数组的长度:用来描述数组容器的容量大小
可以使用数组变量的length属性表示
数组名.length
4.遍历数组:
for(int i = 0;i<数组名.length;i++)
{
数组名[i];
}
5.数组元素的默认初始化值:
(1)整型数组元素的默认初始化值:0
(2)浮点型数组元素的默认初始化值:0.0
(3)字符型数组元素的默认初始化值:'\u0000'
(4)boolean型数组元素的默认初始化值:false
(5)引用数据类型数组元素的默认初始化值:null
6.一维数组的内存解析:
为了提高运算效率,Java虚拟机对空间进行了不同区域的划分,每一片区域都有特定的处理数据方式和内存管理方式
区域名称 | 作用 |
虚拟机栈 | 存储正在执行的每个Java方法的局部变量表等,存放可知长度的各种基本数据类型、对象引用、方法执行完会自动释放 |
堆内存 | 存储对象(包括数组对象),new关键字创建的都存放在堆内存 |
方法区 | 存储已被虚拟机加载的类信息、常量、(静态变量)、即时编译器编译后的代码等数据 |
本地方法栈 | 当程序中调用了native的本地方法后,本地方法执行期间的内存区域 |
程序计数器 | 程序计数器是CPU中的寄存器,包含每一个线程下一条要执行的指令地址 |
数组的内存结构:
四、多维数组:
1.概述:
Java语言里提供了支持多维数组的语法,二维数组可以看成是一维数组array1又作为另一个一维数组array2的元素存在;
2.二维数组的使用:
(1)二维数组的声明与初始化:
静态初始化:数组变量和数组元素同时赋值,此时不需要确定数组的长度
元素类型[][] 数组名 = new 元素类型[][]{{元素1_1,元素1_2,...,元素1_n},{元素2_1,元素2_2,...,元素2_n},...,{元素m_1,元素m_2,...,元素m_n}};
元素类型[][] 数组名 = {{元素1_1,元素1_2,...,元素1_n},{元素2_1,元素2_2,...,元素2_n},...,{元素m_1,元素m_2,...,元素m_n}};
动态初始化:数组变量的赋值和数组元素的赋值分开进行:
元素类型[][] 数组名 = new 元素类型[m][n];
元素类型[][] 数组名 = new 元素类型[m][];
(2)二维数组元素的调用:
调用内层元素:
二维数组名[外层索引][内层索引];
调用内层元素:
二维数组名[外层索引];
(3)二维数组元素的长度:
二维数组元素名.length;
二维数组元素名[外层索引].length;
(4)遍历二维数组:
for(int i = 0;i<二维数组名.length;i++)
{
for(int j = 0;j<二维数组名[i].length;j++)
{
System.out.println(二维数组名[i][j]);
}
}
(5)二维数组元素的默认初始化值:
元素类型[][] 数组名 = new 元素类型[m][n]
外层元素的默认值:相当于一个一维数组的地址值,默认存储地址值
内层元素的默认值:同一维数组的默认初始化值
元素类型[][] 数组名 = new 元素类型[m][];
外层元素的默认值:null
内层元素的默认值:空指针异常报错
五、数组中常见的算法操作:
1.数值型数组特征值的统计:
1.求最大值:
int max = arr[0]
for(int i =0;i<arr.length;i++)
{
if(max<arr[i])
{
max =arr[i];
}
}
2.求最小值:
int min = arr[0]
for(int i =0;i<arr.length;i++)
{
if(max<arr[i])
{
max =arr[i];
}
}
3.求和和平均值:
int sum = 0;
for(int i =0;i<arr.length;i++)
{
sum+=arr[i];
}
int avgValue = sum / arr.length;
2.数组元素的赋值:
杨辉三角:
int[][] yangHui = new int[10][];
for(int i = 0;i<yangHui.length;i++)
{
yangHui[i] = new int[i+1];
yangHui[i] = yangHui[i][i] = 1;
for(int j = 1;j<yangHui[i].length-1;j++)
{
yangHui[i][j] = yangHui[i-1][j] + yangHui[i-1][j-1];
}
}
for(int i =0;i<yangHui.length;i++)
{
for(int j = 0;j<yangHui[i].length;j++)
{
System.out.println(yangHui[i][j]);
}
}
3. 数组的反转:
for(int i = 0;i<arr.length/2;i++)
{
int temp = arr[i];
arr[i] = arr[arr.length-i-1];
arr[arr.length - 1 - i] = temp;
}
4.数组的扩容与缩容
数组扩容:
int[] arr = new int[]{1,2,3,4,5};
int[] newArray = new int[arr.length<<1];
for(int i = 0;i<arr.length;i++)
{
newArray[i] = arr[i];
}
arr = newArray;
数组缩容:
int[] arr = {1,2,3,4,5,6,7};
int deleteIndex = 4;
for(int i = deleteIndex;i<arr.length-1;i++)
{
arr[i] = arr[i+1];
}
arr[arr.length - 1] = 0;
5.元素的查找:
线性查找:算法简单,但是执行效率低,执行复杂度为O(N)
boolean isFlag = true;
for(int i = 0;i<arr1.length;i++)
{
if(target == arr1[i])
{
System.out.println("Found");
isFlag = false;
break;
}
}
if(isFlag)
{
System.out.println("Not Found");
}
二分法查找:要求此数组必须是有序的,执行效率高,时间复杂度为O(logN),相对线性查找较为复杂
int[] arr2 = new int[]{1,2,3,4,5,6,7,8};
int target = 5;
int head = 0;
int end = arr2.length - 1;
boolean isFound = false;
while(head <= end){
int middle = (head + end) / 2;
if(target == arr2[middle])
{
System.out.println("Found");
isFound = true;
break;
}else if(target > arr2[middle])
{
head = middle + 1;
}else
{
end = middle - 1;
}
}
if(!isFound)
{
System.out.println("Not Found");
}
6.数组元素排序:
(1)概述:假设含有n个记录的序列为(R1,R2,...,Rn),其相应的关键字序列为(K1,K2,...,Kn),将这些记录重新排序为(Ri1,Ri2,...,Rin),使得相应的关键字满足条件Ki1<=Ki2<=...<=Kin,这样的一种操作称为排序,排序的目的是达到快速查找;
(2)衡量排序算法的优劣:
a.时间复杂度:分析关键字的比较次数和记录的移动次数,常见的时间复杂度由小到大依次为O(1)<O(log2n)<O(n)<O(nlog2n)<O(n^2)<...<O(2^n)<O(n!)<O(n^n)
b.空间复杂度:排序算法中需要多少辅助内存;
c.稳定性:若两个记录A和B的关键字相等,但排序后A和B的先后次序保持不变,则称这种排序算法是稳定的;否则是不稳定的
(3)排序算法分类:
a.内部排序:整个排序过程不需要借助于外部存储器,所有排序操作都在内存中完成
b.外部排序:参与排序的数据非常多,数据量非常大,计算机无法把整个排序过程放在内存中完成,必须借助于外部存储器,最常见的是多路并归排序;
(4)十大内部排序算法:
排序方法 | 时间复杂度(平均) | 时间复杂度(最坏) | 时间复杂度(最好) | 空间复杂度 | 稳定性 |
插入排序 | O(n^2) | O(n^2) | O(n) | O(1) | 稳定 |
希尔排序 | O(n^1.3) | O(n^2) | O(n) | O(1) | 不稳定 |
选择排序 | O(n^2) | O(n^2) | O(n^2) | O(1) | 不稳定 |
堆排序 | O(nlog2n) | O(nlog2n) | O(nlog2n) | O(1) | 不稳定 |
冒泡排序 | O(n^2) | O(n^2) | O(n) | O(1) | 稳定 |
快速排序 | O(nlog2n) | O(n^2) | O(nlog2n) | O(nlog2n) | 不稳定 |
并归排序 | O(nlog2n) | O(nlog2n) | O(nlog2n) | O(n) | 稳定 |
计数排序 | O(n+k) | O(n+k) | O(n+k) | O(n+k) | 稳定 |
桶排序 | O(n+k) | O(n^2) | O(n) | O(n+k) | 稳定 |
基数排序 | O(n*k) | O(n*k) | O(n*k) | O(n+k) | 稳定 |
冒泡排序:
1.比较相邻的元素。如果第一个比第二个大(升序),就交换他们两个。
2.对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对.这步做完后,最后的元素会是最大的数
3.针对所有的元素重复以上的步骤,除了最后一个。
4.持续每次对越来越少的元素重复上面的步骤,直到没有任何一对数字需要比较为止;
int[] arr = {1,3,5,2,4,6,7,9,8};
for(int j = 0;j<arr.length - 1;j++)
{
for(int i = 0;i<arr.length - 1 - j;i++)
{
if(arr[i]>arr[i+1])
{
int temp = arr[i+1];
arr[i+1] = arr[i];
arr[i] = temp;
}
}
}
快速排序:
1.从数列中挑出一个元素,称为"基准"(pivot);
2.重新排序数列,所有元素比基准值小的摆放在基准前面,所有元素比基准值大的摆在基准的后面(相同的数可以到任一边)。在这个分区结束之后,该基准就处于数列的中间位置。这个称为分区(partition)操作。
3.递归地(recursive)把小于基准值元素的子数列和大于基准值元素的子数列排序。
4.递归的最底部情形,是数列的大小,也就是永远都已经被排序好了。虽然一直递归下去,但是这个算法总会结束,因为在每次的选代(iteration)中它至少会把一个元素摆到它最后的位置去。
六、Arrays工具类的使用:
java.util.Arrays类即为操作数组的工具类,包含了用来操作数组的各种方法,比如:
1.数组元素拼接:
static String toString(int[] a):字符串表示形式由数组的元素列表组成,括在方括号中。相邻元素用字符', '分隔;
static String toString(Object[] a):字符串表示形式由数组的元素列表组成,括在方括号中。相邻元素用字符', '分隔;
元素将自动调用自己从Object继承的toString方法将对象转为字符串进行拼接,如果没有重写,将返回类型@hash值,如果重写则按重写返回的字符串进行拼接;
2.数组排序:
static void sort(int[] a):将a数组按照从小到大进行排序
static void sort(int[] a, int fromIndex, int toIndex):将a数组的[fromIndex,toIndex)部分按照升序排列
static void sort(Object[] a):根据元素的自然顾序对指定对象数组按升序进行排序
static <T>void sort(T[] a,Comparator<? super T>c):根据指定比较器产生的顺序对指定对象数组进行排序;
3.数组元素的二分查找
static int binarySearch(int[] a, int key)
static int binarySearch(Object[]a,Object key)
要求数组有序,在数组中查找key是否存在,如果存在返回第一次找到的下标,不存在返回负数。
4.数组的复制:
static int[] copyOf(int[] original,int newLength):根据original原数组复制一个长度为newLength的新数组,并返回新数组
static <T> T[] copyOf(T[] original,int newLength):根据original原数组复制一个长度为newLength的新数组并返回新数组
static int[] copyOfRange(int[] original, int from, int to):复制original原数组的[from,to)构成新数组,并返回新数组;
static <T>T[] copyOfRange(T[] original,int from,int to):复制original原数组的[from,to)构成新数组,并返回新数组
5.比较两个数组是否相等
static boolean equals(int[] a,int[] a2):比较两个数组的长度、元素是否完全相同;
static boolean equals(Object[] a,Object[] a2):比较两个数组的长度、元素是否完全相同;
6.填充数组:
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 tolndex, Object val):将a数组[fromIndex,toIndex)部分填充为val对象
七、数组使用中常见的异常:
1.数组角标越界异常:ArrayIndexOutOfBoundsException
2.空指针异常:NullPointerExeception
情况1:
int[] arr1 = new int[10];
arr1 = null;
System.out.println(arr1[0]);
情况2:
int[][] a = new int[10][];
System.out.println(a[0][0]);
一旦程序执行中出现了异常,程序就会终止执行,需要针对异常提供的信息修改对应代码,避免异常再次出现;