递归
概述
递归 : 就是在当前方法中, 调用自己
基本思想 : 以此类推
递归和迭代是等价的 , 而迭代就是循环 , 所以递归也是重复做某件事
三要素 : 初始值 , 终止条件 , 步长
如果循环能做到的 , 就不要使用递归 , 因为递归效率低 , 比较耗内存
应用场景 :
一般树状结构 , 需要使用递归来完成
比如菜单目录 , 每一层目录结构都是一个循环 , 两层就需要嵌套循环 , 那么当不知道有多少层目录的时候 循环就不行了
常见异常
public static void main(String[] args){
m1();
}
pubic static void m1(){
//没有终止条件
m1();
}
数组
概述
数组 : 是引用数据类型 (类 , 接口 , 数组)
数组是底层的数据结构 , 几乎任何语言都有 , 数组被分为 索引数组 和 关联数组
数据结构
数据结构是计算机储存 、组织数据的方式 .数据结构是指相互之间存在一种或多种特定关系的数据元素的集合
数组
栈
链表
散列
二叉树 / 红黑树
应用场景
数组用来存储多个数据 , 比如单个成绩可以用一个变量存储 ,多个的话写多个变量就不合适
可以使用数组 , 来存储多个数据 , 这样一个变量就可以代替多个变量 , 方便统一操作 , 但是数组中的元素 数据类型必须一致
int i = 2;
int[] is = {1,3,5,4,789,456};
数组特性
可以看做是一个多个相同数据的一个存储容器 , 可以对这些数据进行统一管理
是一种引用数据类型
意味着 数组占用两块空间 , 栈内存 变量保存 堆内存 对象的引用
是一种线性数据结构 , 内存空间是连续的 , 类似于单元楼
数据可以保存任意数据的元素, 但是每一维的元素类型必须一致
数组的长度不能直接更改 , 也就意味着数组一旦确定就不能添加或减少
除非新建一个新的数组, 然后把原数组中的元素复制进去 ,在复制的过程中进行 添加和删除
数组中有一个 length 属性 , 保存的是数组的长度
并且数组中元素都有一个独一无二的编号(下标)
要查找数组中的某个元素的时候 , 只需要使用对应的索引下标即可 , 在数组中 0 是第一个元素的下标 , 1则是第二个元素的下标 , 以此类推
这种通过内存地址直寻法 , 效率极高
操作 : 增删改查
结合数组特性, 数组 查找和更改效率高 , 添加和删除 效率相对较低
与数组相比 , 链表的 添加和删除的效率高 , 查找和更改效率低
数组声明
静态声明 : 在知道数组每个元素的值的时候 , 使用静态声明
数据类型[] 变量 = {值,值,值,......};
int i = 5;
int[] is = {1,23,4,5,6};
int[][] is2 = {{1,2,3,4,5}; {1,2,3,4,5}; {1,2,3,4,5}; {1,2,3,4,5}; ......};
动态声明 : 在预先不知道每个元素值是多少的时候 , 使用动态声明 , 需要提前指定数组空间长度 , 并使用默认值占位
整数 : 0
浮点 : 0.0
char : \u0000
boolean : false
引用类型 : null
数据类型[] 变量 = new 数据类型[长度];
int[] is = new int[10];
数组传递 , 第三种声明方式 静态声明
数据类型[] 变量 = new 数据类型[]{值,值,值,......};
public static void main(String[] args){
int [] arr = {1,2,3};
m1(arr);
//数组字面量传递
m1(new int[]{1,2,3});
}
public static void m1(int[] arr){
for(int i = 0; i < arr.length; i++){
System.out.println(arr[i]);
}
}
数据类型[] 变量 和 数据类型 变量[]
int[] i = {1,2,3} int i[] = {1,2,3}
数组的基本操作
获取数据(查)
//长度
System.out.println(is.length);
//首元素 索引/下标 index
//数组对象(数组变量) [下标]
System.out.println(is[0]);
//尾元素
System.out.println(is[is.length-1])
设置数据(改)
//更改操作
//数组[下标] = 值;
is[1] = 1;
遍历
int[] arr = new int[10];
arr[0] = 100;
for(int i = 0; i < arr.length; i++){
System.out.println(arr[i]);
}
//增强for循环
//把arr数组中的元素 依次取出 并赋值给i
for(int i : arr){
System.out.println(i);
}
常见异常
1. 下标越界异常 java.lang.ArrayIndexOutofBoundsException
比如数组中就三个元素, 最后一个下标就是2 ,而要输出下标为3或直接负数 就不行
2. 空指针异常 java.langNullPointerException
使用null值 , 访问属性 , 因为是null ,所以没有堆内存存数据空间
所以是找不到数据对象的 , 自然就没办法操作数组
null 什么都没有 , 堆内存对象也没有
null 和 长度为 0 是两个概念
public static void main(String[] args){
int[] arr1 = {1,2};
// java.lang.ArrayIndexOutofBoundsException: 2
System.out.println(arr1[2]);
int[] arr2 = null;
// java.langNullPointerException
System.out.println(arr2[0]);
}
数组传递
传值 和 传引用
传值 : 基本类型 传递 传递的是值
引用 : 引用类型 传递 传递的是地址
int i = 10;
m1(i);
System.out.println(i);
System.out.println("--------------");
int[] arr = { 1, 2, 3, 4, 5};
m2(arr);
System.out.println("--------------");
for(int j = 0; j < arr.length; j++){
System.out.println(arr[j]);
}
public static void m1(int i){
i = 20;
System.out.println(i);
}
public static void m2(int[] i){
i[0] = 20;
for(int j = 0; j < arr.length; j++){
System.out.println(i[j]);
}
}
数组复制
实现
/**
*需求 : 把a数组中的某些数据 复制到 b数组中,并替换指定数据
*a = {1,2,3,4,5,6};
*b = {11,12,13,14,15,16};
*
*复制操作
*b = {11,2,3,4,15,16}
*
*a,1b,1,3
*数组替换式复制
*
*@param src
* 源数组
*@param srcPos
* 源数组起始位置(包含)
*@param dest
* 目标数组
*@param destPos
* 目标数组起始位置(包含)
*@param length
* 复制个数
*
*因为传引用, 并且也没有更改dest的长度 , 所以不需要返回值
*/
public static void arrayCopy(int[] src, int srcPos, int[] dest, int destPos, int length){
for(int i = 0; i < length; i++){
dest[destPos++] = src[srcPos++];
}
}
int[] src = {1,2,3,4,5,6};
int[] dest = {11,12,13,14,15,16};
arrayCopy(src, 1, dest, 2, 3);
for(int i = 0; i < dest.length; i++){
System.out.println(dest[i]);
}
APL
并不是什么功能都要自己去编写和实现
很多常用的功能 , 都已经写好了直接调用即可 就比如说刚才编写的数组替换复制
//源数组,起始索引(包含),目标数组,起始索引(包含),复制个数
System.arraycopy(sec, 1, dest, 2, 3);
插入式复制
插入式复制 , 肯定需要更改长度大小 , 所以要新建数组
所以要把新数组返回
所以需要把新数组返回
1 新数组长度
2 复制逻辑
1 目标数组 起始位置和之前的数据 先复制到新数组中
2 把源数组起始位置开始 复制length个 到新数组中
3 目标数组 起始位置之后的数据复制到新数组中
3 返回新数组
二维数组
声明方式
// 静态声明二维数组
int[][] arr = {
{1,2,3},
{11,12,13,14},
{0},
{1,11,13,25,769,9,9,9,45}
};
二维数组的动态声明 :
int[][] arr = new int[5][3];
二维数组中 有5个一维数组 , 并且每个一维数组中有3个元素
public static void main(String[] args){
int[][] arr = new int[5][3];
arr[2][2] = 1;
for(int i = 0; i < arr.length; i++){
System.out.print(arr[i][j]+" ");
}
System.out.println();
}
int[][] arr = new int[5][];
二维数组中有5个一维数组 , 且每个一维数组都是空的
需要单独对每个一维数组进行设置
public static void main(String[] args){
int[][] arr1 = new int[5][];
//初始化一维数组
for(int i = 0; i < arr1.length; i++){
arr1[i] = new int[i+1];
}
for(int i = 0; i < arr1.length; i++){
for(int j = 0; j < arr1[i].length; j++){
System.out.print(arr1[i][j]+" ");
}
System.out.println();
}
}
使用方式
//获取第一个一维数组
int[] arr0 = arr[0];
int arr00 = arr0[0];
System.out.println(arr00);
//第一个数组中的第一个元素
System.out.println(arr[0][0]);
//最后一个数组中的最后一个元素
System.out.println(arr[3][4]);
int[] arr3 = arr[arr.length-1];
int arr34 = arr3[arr3.length-1];
System.out.println(arr[arr.length-1][arr[arr.length-1].length-1]);
//遍历二维数组
for(int i = 0; i < arr.length; i++){
//int[] arri = arr[i];
for(int j = 0; j < arr[i].length; j++){
System.out.print(arr[i][j]+" ");
}
System.out.println();
}
Scanner
//接收用户输入
Scanner s = new Scanner(System.in);
//程序执行到这里,就会进入等待状态,等待用户输入
//并把输入的数据 返回 并赋值给变量suerInput
//多个数据以空格隔开
String userInput = s.next();
System.out.println(userInput+“-----------”);
//接受一行数据,不需要分隔符
String userInput = s.nextLine();
Syetem.out.println(userInput);
//接收整型,必须是纯数字,小数点也不行 java.util.InputMismatchException
int userInput = s.nextInt();
Syetem.out.println(userInput);
//接收小数,可以有一个小数点
double userInput = s.nextDouble();
System.out.println(userInput);
排序
交换变量的值
// 1 中间变量(开发常用)
int x = 10;
int y = 20;
int temp = x;
x = y;
y = temp;
System.out.println(x+":"+y);
// 2 位移运算(面试常用)
//转换为对应的二进制,然后每位进行疑惑,相同为0,不同为1
//0010
//0011
x = 2;
y = 3;
x = x ^ y;
//0001
//0011
y = x ^ y;
//0001
//0010
x = x ^ y;
//0011
//0010
System.out.println(x+":"+y);
// 3 加减运算
x = 10;
y = 20;
x = x + y;
y = x - y;
x = x - y;
System.out.println(x+":"+y);
冒泡排序
1 比较相邻的两个元素 , 如果第一个比第二个大,就交换位置
2 对每一对相邻的元素做相同的比较工作 , 这样比较完一轮之后 , 最后一个元素一定是最大的
3 再重复执行上面的步骤, 除了最后一个元素(比较次数依次递减)
4 一直到没有任何一对元素 , 终止
嵌套循环
1 外层循环决定比较多少轮
2 内层循环决定每轮比较的次数
public static void main(String[] args){
int[] arr = {1,2,3,4,5,7};
bubbleSort(arr);
for(int i = 0; i < arr.length; i++){
System.out.println(arr[i]);
}
}
public static void bubbleSort(int[] arr){
for(int i = 0; i < arr.length-1; i++){
for(int j = 1; j < arr[i].length-1-i; j++){
if(arr[j] < arr[j+1]){
int temp = arr[j];
arr[j] = arr[j+1];
arr[j+1] = temp;
}
}
}
}
选择排序
每次都把其中最小的元素放到左边
public static void selectSort(int[] arr){
for(int i = 0; i < arr.length; i++){
//min是最小的元素下标,默认假设i就是最小的
int min = i;
for(int j = i+1; j < arr.length; j++){
//有比min还小的数的时候,就把该数的下标赋值给min
if(arr[min > arr[j]){
min = j;
}
}
}
//判断i是否是最小的
//只要min不等于i说明 有比i小的
if(min != i){
int temp = arr[min];
arr[min] = arr[i];
arr[i] = temp;
}
}
API
int[] arr = {1,5,4,2,3};
Arrays.sort(arr);
for(int i = 0; i < arr.length; i++)
System.out.println(arr[i]);
类、对象、构造器、封装
面向对象
概述 :
面向对象是软件开发方法,一种编程范式. 面向对象是一种对现实世界理解和抽象的方法 , 是计算机编程技术发展到一定阶段后的产物
面向对象是相对于面向过程来讲的 , 面向对象方法 , 吧相关的数据和方法组织为一个整体来看待 , 从更高的层次来进行建模,更贴近事物的自然运行模式
面向过程
侧重分步骤 , 类似于公司的扁平化管理
比如 五子棋
面向过程的设计思想就是首先分析问题 :
1 开始游戏
2 黑子先走
3 绘制画面
4 判断输赢
5 白子走
6 绘制画面
7 判断输赢
8 重复进行 , 依次类推
面向对象
侧重分模块 , 类似于公司的层级管理
比如五子棋
用面向对象的思维来思考
五子棋可分为 :
1 白黑双方 , 行为规范一直 , 形成棋子系统
2 棋盘系统 绘制画面
3 规则系统 比如判断输赢 是否违规等操作
面向对象是以功能划分的 而不是步骤划分 类似于炒饭和盖饭,盖饭就等于 是面向对象,饭菜分离,可以任意组合,耦合度降低
优点 : 维护简单 , 可扩展性 , 可重用性
类和对象
对象 :
代码角度 : new的实例化某一个类的实例 , 封装特有的数据
数据角度 : 封装数据和逻辑的一种方式
人类认知角度 : 对象就是具体的个体 , 一切皆对象
设计角度 : 从一个实际的实体抽象某些属性的一种实体表示
概念/ 定义 : 使我们在思想上对某一个东西 , 或者某一类东西的唯一性标识
描述了这一类事物的特性
是我们对客观事物描述的一个标准和模板
类定义标准和模板 , 而对象就是符合这个表示这个标准的个体
我们抽离某个概念 , 就能建立相关事物的类 , 一定通过类中的属性来形成这个概念 , 然后通过这些个属性来形成类 , 通过不同的属性值来形成不同的个体(对象)
通过不同的属性 , 划分不同的类, 通过不同的属性值 , 划分不同的个体
从类到对象的过程 , 可以理解为对成员变量赋值的过程
堆内存 :
每个对象空间分为3块
1 数据区域 : 成员变量
2 头部 : hash值
3 类型 : 静态区类文件的空间引用
什么时候使用静态变量 , 什么时候使用成员变量
静态变量是类级别的 , 对象无关 , 静态变量的属性和值 , 是所有对象都一致
成员变量是对象级别的 , 它们拥有相同的属性 , 但值可以相同也可以不同
对象的使用和特征
JVM特性
跨平台 多线程 自动垃圾回收 面向对象
垃圾 : 没有更多引用指向这个对象(谁也找不到) , 该对象被视为垃圾数据 等待被回收
从类到对象的过程 , 可以理解为对成员变量赋值的过程
面向对象特性
继承性 封装性 多态性 抽象
对象使用
//创建对象
Star star = new Star();
//调用成员变量
System.out.println(star.age);
//没有name变量,所以不可以调用
//System.out.println(star.name);
star.test();
//所以引用数据类型都可以赋值为null
star = null;
//使用null 访问成员属性 会报错 空指针异常
System.out.println(star.age);
常见异常
空指针是运行时异常 , 编译时不报错,一般因为粗心导致
空指针 , 只要是空指针 , 说明使用null 调用了成员属性
JavaBean
1 公共类
2 私有属性
3 getter/setter 方法
4 构造方法
private int age;
String addr;
String sex;
public void setAge(int age1){
if(age1 <= 0)
age = 1;
else
age = age1;
}
public int getAge(){}
传引用
传值和传引用
传值 : 传递基本数据类型
传引用 : 传递引用数据类型
public static void main(String[] args){
int i = 10;
m1(i);
System.out.println(i);
System.out.println("-----------");
Animal a = new Animal();
a.age = 10;
m2(a);
System.out.println(a.age);
}
public static void m1(int i){
i++;
System.out.println(i);
}
public static void m2(Animal a){
// a = new Animal();
a.age++;
System.out.println(a.age);
}
构造方法
编译器功能 :
1 把java文件编译成class文件
2 检查语法结构是否合法
3 帮我们把不规范的代码,补齐
比如 静态变量,需要类名调用,但是一般我们调用当前类中的静态变量的时候,不会加类名
此时 编译器 就会自动帮我们加上(如果我们不加类名访问,编译器自动帮我们加上当前类的类名)
另外就是构造方法,如果类中没有显示声明构造方法的话,则编译器会自动帮我们创建一个公共的无参构造
构造方法 : 创建当前类的实例化对象,并初始化成员属性
语法 : [权限修饰] 类名(参数列表){ 方法体 } public,private这些
如果我们不定义构造方法,则默认有一个公共的无参构造( public 类名(){} )
一旦我们指定了构造方法,则默认的无参构造就没有了 所以 一般我们需要指定构造方法的时候,要考虑再写一个无参构造
构造方法重载 : 可以根据需求,编写多个构造方法,方法名相同,参数列表不同(个数,类型)
class Student{
Sting name;
int age;
/// 需求 : 学生必须有name属性的值, age可以有,可以没有
public Student(String name1){
name = name1;
}
public Student(String name1,int age1){
name = name1;
age = age1;
}
}
构造方法和成员方法
public class Constructor_02{
public void Constructor_02(int i){}
public Constructor_02(){}
}
public static void main(String[] arge){
new Constructor_02();
// 构造方法可以和成员方法同名
// 就看返回值即可 , 因为构造方法没有返回值,脸void都没有
// new COnstructor_02(10);
}
This
This是什么
this关键字 :
this 是每个对象中 , 保存自身内存地址的一个引用类型的成员变量
this 就表示这个对象 , this 的类型就是当前类的类型
当前类: this所在的类 , 就是当前类
只能出现在成员方法和构造方法中 , 不能出现在静态方法中
谁调用的这个成员方法 , this就是谁
能干什么
功能 :
1 用在成员方法/构造方法中 , 用来区别同名的成员变量和局部变量 this.xxxxx
2 用在构造方法中 , 重载调用当前类的其它构造方法
this(参数); 必须写在构造方法的第一行
3 return this ; 可以返回当前对象的内存地址 , 以此做到链式调用
This怎么用
区分局部变量和成员变量
class MyDate{
int year;
int month;
int day;
public void setDay(int day){
this.day = day;
}
public MyDate(int year, int month, int day){
this.year = year;
this.month = month;
this.day = day;
}
}
如何区分同名的局部变量和静态变量? 类名.静态变量名
如何区分同名的局部变量和成员变量? this.成员变量名
重载调用其他构造方法
public MyDate(){
//this.year = 1970;
//this.month = 1;
//this.day = 1;
//重载调用下面的有参构造,必须在构造方法的第一行
this(1970,1,1)
}
public MyDate(int year, int month, int day){
this.year = year;
this.month = month;
this.day = day;
}
链式调用
public static void main(String[] args){
This_03 t = new This_03();
t.m1();
t.m2();
// 因为m1返回值是This_03类型,所以须使用This_03来接收
This_03 t2 = t.m1();
t2.m2();
// 返回this可以链式调用当前类中的成员方法
t.m1().m2();
// 返回其他类的对象 , 就可以链式调用其他类中的方法
t.m3().cahrAt(1);
}
public String m3(){
return "giuhnfkjsnjifhi211125";
}
public This_03 m1(){
System.out.println(1);
return this;
}
public This_03 m2(){
System.out.println(2);
}