回顾与重点
模块八回顾
-
封装:
a. 将细节隐藏起来,不让外界直接调用,再提供接口,供外界通过公共接口间接使用隐藏起来的细节
b. 代表性的:
将一段代码放到一个方法中(隐藏细节),通过方法名(提供的公共接口)去调用
private 关键字 → 私有的,被private修饰之后别的类不能直接调用,只能再当前类中使用
c. get/set方法
set方法:为属性赋值
get方法:获取属性值
d. this关键字:代表当前对象,哪个对象调用this所在的方法,this就代表哪个对象
区分重名的成员变量和局部变量
-
构造:
a. 无参构造:new 对象
特点:jvm会自动为每个类提供一个无参构造
b. 有参构造:new 对象 为属性赋值
特点:如果手写了有参构造,jvm将不再提供无参构造,所以建议都写上
-
标准JavaBean:
a. 类必须是公共的,具体的
b. 必须是私有的
c. 必须有构造方法(有参、无参)
d. 必须有get/set方法
快捷键:alt + insert
模块九重点
- 会定义静态成员以及会调用静态成员
- 会使用可变参数(会给可变参数传参)
- 会二分查找(手撕)
- 会冒泡排序(手撕)
- 会debug的使用
第一章 static 关键字
1.1 static的介绍以及基本使用
-
概述:static 是一个静态关键字
-
使用
a. 修饰一个成员变量
static 数据类型 变量名
b. 修饰一个方法
修饰符 static 返回值类型 方法名(形参) { 方法体 return 结果 }
-
调用静态成员:
类名直接调用(不用new对象)
-
静态成员特点:
a. 静态成员属于类成员,不属于对象成员(非静态的成员属于对象成员)
b. 静态成员会随着类的加载而加载
c. 静态成员优先于非静态成员存在在内存中
d. 凡是根据静态成员所在的类创建出来的对象,都可以共享这个静态成员
public class Student { String name; int age; static String classRoom; }
public class Demo01Static { public static void main(String[] args) { // 先给静态成员赋值 静态成员属于类 非静态成员属于对象 Student.classRoom = "111"; Student s1 = new Student(); s1.name = "郭靖"; s1.age = 28; System.out.println(s1.name+","+s1.age+","+Student.classRoom); System.out.println("==========="); Student s2 = new Student(); s2.name = "黄蓉"; s2.age = 26; System.out.println(s2.name+","+s2.age+","+Student.classRoom); } }
1.2 static 成员的内存说明
1.3 static 修饰成员的访问特点
-
在静态方法中能直接访问非静态成员吗?
不能,想要调用的话:new 对象
-
在非静态方法中能直接访问静态成员吗?能
a. 同类中非静态成员访问静态成员:
直接调用:方法名()
类名调用:类名.方法名()
b. 不同类中非静态成员访问静态成员:
类名调用:类名.方法名() → 推荐
new 对象调用:new 对象().方法名() → 本质上就是类名调用,如下图所示
-
在静态方法中能直接访问静态成员吗?能
a. 同类中静态成员访问静态成员:
直接调用:方法名()
类名调用:类名.方法名()
b. 不同类中静态成员访问静态成员:
类名调用:类名.方法名() → 推荐
-
在非静态方法中能直接访问非静态成员吗?能
a. 同类中非静态成员访问非静态成员:
直接调用:方法名()
new 对象调用:new 对象().方法名()
b. 不同类中非静态成员访问非静态成员:
new 对象调用:new 对象().方法名()
public class Person { // 非静态方法 public void eat() { System.out.println("人要干饭"); } // 静态方法 public static void drink() { System.out.println("人要喝水"); } }
public class Demo01Static { // 静态方法 public static void main(String[] args) { // 1. 静态成员中访问非静态成员 // 同类 Demo01Static demo01Static = new Demo01Static(); // new 对象调用 demo01Static.method03(); // 不同类 Person person = new Person(); // new 对象调用 person.eat(); } // 静态方法 public static void method01() { // 3. 静态方法中访问静态成员 // 同类 method02(); // 直接调用 // 不同类 Person.drink(); // 类名调用 } // 静态方法 public static void method02() { } // 非静态方法 public void method03() { // 2. 非静态成员访问静态成员 // 同类中 method01(); // 直接调用 → 推荐 // 或者 Demo01Static.method01(); // 类名.方法名() // 不同类中 → 不能直接调用 Person.drink(); // 类名调用 → 推荐 Person person = new Person(); // new 对象调用 本质上就是类名调用 person.drink(); } // 非静态方法 public void method04() { // 4. 非静态方法中访问非静态方法 // 同类 method03(); // 直接调用 new Demo01Static().method03(); // new 对象调用 // 不同类 new Person().eat(); // new 对象调用 } }
总结:
- 不管在不在同一个类中,非静态成员都可以new 对象调用
- 不管在不在同一个类中,静态成员都可以类名调用
1.4 static 成员使用场景
问题1:既然static成员那么好使(类名直接调用),那么我们实际开发中,能不能将所有的成员都定义成静态的呢?不能
原因:由于静态成员会随着类的加载而加载,如果将所有的成员都变成静态的,那么类一加载,静态成员都会进内存,会大量占用内存空间。
问题2:那么静态成员啥时候定义呢?
一般情况下,我们在抽取工具类的时候可以将工具类中的所有成员都定义成静态的。
问题3:啥时候定义工具类?
比如我们在写代码的过程中,发现有的功能在反复实现,代码一样,功能一样,此时就可以抽取出来,形成工具类。
public class ArraysUtils {
/*
构造方法用 private 修饰
工具类中的成员都是静态的,静态成员都是类名调用,不需要new对象
所以工具类中的构造方法都是用 private修饰
如果构造方法被 private 修饰,那么在别的类中,就不能用构造方法new对象
*/
// 无参构造
private ArraysUtils() {
}
/**
* 定义一个方法,实现获取 int 数组最大值
* @param arr 数组
* @return 数组中的最大值
*/
public static int getMax(int[] arr) {
int max = arr[0];
for (int i = 1; i < arr.length; i++) {
if (max<arr[i]) {
max = arr[i];
}
}
return max;
}
}
public class Demo01Utils {
public static void main(String[] args) {
int[] arr = {5,6,8,9,2,1,3};
/*int max = arr[1];
for (int i = 1; i < arr.length; i++) {
if (max<arr[i]) {
max = arr[i];
}
}
System.out.println(max);*/
int max = ArraysUtils.getMax(arr);
System.out.println("max = " + max);
}
}
public class Demo02Utils {
public static void main(String[] args) {
int[] arr = {5,6,8,9,52,1,3};
/*int max = arr[0];
for (int i = 1; i < arr.length; i++) {
if (max<arr[i]) {
max = arr[i];
}
}
System.out.println(max);*/
int max = ArraysUtils.getMax(arr);
System.out.println("max = " + max);
}
}
第二章 可变参数
-
需求:
定义一个方法,实现n个整数相加
-
分析:
方法参数位置,只明确了参数的类型,但是不明确参数个数,此时就可以定义成可变参数
2.1 可变参数的使用和注意事项
-
定义格式
数据类型...变量名
-
注意
a. 可变参数的本质是一个数组
b. 参数位置不能连续写多个可变参数,而且当可变参数和其他参数一起使用时,可变参数需要放到参数列表最后。
public class Demo01Var { public static void main(String[] args) { sum(1,2,3,4,5); sum1(1, 1,2,3,4,5); } public static void sum(int...arr) { int sum = 0; for (int i = 0; i < arr.length; i++) { sum += arr[i]; } System.out.println(sum); } // 当可变参数和其他参数一起使用时,可变参数需要放到参数列表最后 public static void sum1(int i, int...arr) { } }
2.2 可变参数的练习(字符串拼接)
需求:返回n个字符串拼接结果,如果没有传入字符串,那么返回空字符串“”。
public class Demo02Var {
public static void main(String[] args) {
String concat = concat("张无忌", "张三丰", "张山");
System.out.println("concat = " + concat);
}
public static String concat(String...arr) {
String str = "";
for (int i = 0; i < arr.length; i++) {
str += arr[i];
}
return str;
}
}
需求:n个字符窜进行拼接,没个字符窜之间使用某字符进行分隔,如果没有传入字符串,那么返回空字符串“”。
比如:
concat("-", "张无忌", "张三丰", "张三") → 返回 → 张无忌-张三丰-张三
public class Demo03Var {
public static void main(String[] args) {
String concat = concat("-","张三丰", "张翠山", "张无忌");
System.out.println("concat = " + concat);
}
public static String concat(String regex,String...arr) {
String str = "";
for (int i = 0; i < arr.length; i++) {
if (i== arr.length-1) {
str += arr[i];
} else {
str += arr[i] + regex;
}
}
return str;
}
}
第三章 递归
3.1 递归的介绍和基本使用
-
概述:方法内部自己调用自己
-
分类:
a. 自己递归
public static void method() { method() }
b. 间接递归
A() { B() } B() { C() } C() { A() }
-
注意:
a. 递归必须要有出口,否则会出现”栈内存溢出“。
public class Demo01Recursion { public static void main(String[] args) { method(); } public static void method() { method(); } }
b. 递归即使有出口,递归次数也不要太多。
3.2 递归练习_输出3-1
源码:
public class Demo02Recursion {
public static void main(String[] args) {
method(3);
}
public static void method(int n) {
/*System.out.println("n = " + n);
n--;
if (n >= 1) {
method(n);
}*/
if (n == 1) {
System.out.println("n = " + n);
return;
}
System.out.println("n = " + n);
n--;
method(n);
}
}
执行过程:
3.3 递归练习_n的阶乘
需求:定义一个方法,完成3的阶乘
3*2*1
分析:假如定义一个方法,代表n的阶乘 → method(n) → n接收多少,就代表多少的阶乘
method(1) 1
method(2) 2 * 1 → 2 * method(1)
method(3) 3 * 2 * 1 → 3 * method(2)
method(n) → n * method(n-1)
源码:
public class Demo03Recursion {
public static void main(String[] args) {
int result = method(3);
System.out.println("result = " + result);
}
public static int method(int n) {
if (n == 1) {
return 1;
}
return n * method(n-1);
}
}
3.4 递归练习_斐波那契数列
不死神兔
故事得从西元1202年说起,话有一位意大利青年,名叫斐波那契。
在他的一部著作中提出一个有趣的问题:假设一对刚出生的小兔,一个月后就能长成大兔,再过一个月就能生下一对小兔,并且此后每个月都生一对小兔,一年内没有发生死亡。
问:一对刚出生的兔子,一年内繁衍成多少对兔子?
规律:一个数等于前两个数之和,比如:1 1 2 3 5 8 13 21 34 55…
-
假设:定义一个方法method,参数传递month,代表月份
-
分析
method(1) 1 method(2) 1 method(3) 2 → method(1) + method(2) method(4) 3 → method(2) + method(3) method(5) 5 → method(3) + method(4) method(6) 8 → method(4) + method(5) method(n) → method(n-2) + method(n-1)
源码:
public class Demo04Recursion { public static void main(String[] args) { int result = method(12); System.out.println("result = " + result); } public static int method(int n) { if (n == 1 || n == 2) { return 1; } return method(n-2) + method(n-1); } }
第四章 数组常见算法
4.1 数组翻转
-
概述:数组对称索引位置上的元素互换
4.1.1 代码实现
public class Demo01Array { public static void main(String[] args) { int[] arr = {1,2,3,4,5,6,7}; for (int min = 0, max = arr.length - 1; min < max; min++, max--) { int temp = arr[min]; arr[min] = arr[max]; arr[max] = temp; } System.out.println("arr = " + Arrays.toString(arr)); // arr = [7, 6, 5, 4, 3, 2, 1] } }
4.2 冒泡排序
数组的排序,是将数组中的元素按照大小进行排序,默认都是以升序的形式排序,数组的排序的方法有很多,我们讲解的是数组的冒泡排序。
排序,都要进行数组元素大小的比较,再进行位置的交换。冒泡排序法是采用数组中相邻元素进行比较换位。
4.2.1 代码实现
import java.util.Arrays;
public class Demo02Bubble {
public static void main(String[] args) {
// 定义一个数组
int[] arr = {5,4,3,2,1};
/*
第一圈
越界原因:当i变化到4的时候 → arr[4] > arr[5] → 直接操作勒5索引,所以越界了
越界解决:我们可以让arr.length - 1
如果arr.length-1 → 比较就是 i<4 → 此时i最大可以变化到3
当i变化到3时 → arr[3]>arr[4] → 正好时最后两个元素进行比较
*/
/*for (int i = 0; i < arr.length-1-0; i++) {
if (arr[i]>arr[i+1]) {
int temp = arr[i];
arr[i] = arr[i+1];
arr[i+1] = temp;
}
}
for (int i = 0; i < arr.length-1-1; i++) {
if (arr[i]>arr[i+1]) {
int temp = arr[i];
arr[i] = arr[i+1];
arr[i+1] = temp;
}
}
for (int i = 0; i < arr.length-1-2; i++) {
if (arr[i]>arr[i+1]) {
int temp = arr[i];
arr[i] = arr[i+1];
arr[i+1] = temp;
}
}
for (int i = 0; i < arr.length-1-3; i++) {
if (arr[i]>arr[i+1]) {
int temp = arr[i];
arr[i] = arr[i+1];
arr[i+1] = temp;
}
}*/
/*
外层循环代表比较了几圈
n-1圈
*/
for (int i = 0; i < arr.length-1; i++) {
/*
内层循环代表每一圈比较的次数
每圈都少比较一次
*/
for (int j = 0; j < arr.length-1-i; j++) {
if (arr[j] > arr[j+1]) {
int temp = arr[j];
arr[j] = arr[j+1];
arr[j+1] = temp;
}
}
}
System.out.println("Arrays.toString(arr) = " + Arrays.toString(arr));
}
}
4.3 二分查找
-
前提:数组中的数据必须是有序的
-
查询思想
a. 老实查询:遍历数组,一个一个比较 → 查询效率慢
b. 二分查找:每次找中间索引对应的元素进行比较查询(每一次查询少一半数据)
4.3.1 代码实现
public class Demo03Binary {
public static void main(String[] args) {
int[] arr = {1,2,3,4,5,6,7,8,9};
int index = binary(arr, 9);
System.out.println("index = " + index);
}
public static int binary(int[] arr, int data) {
// 定义三个变量,分别代表最大索引,最小索引,中间索引
int min = 0;
int max = arr.length-1;
int mid = 0;
// 查找
while (min<=max) {
mid = (min+max)/2;
if (data>arr[mid]) {
min = mid + 1;
} else if (data<arr[mid]) {
max = mid -1;
} else {
return mid;
}
}
return -1;
}
}
第五章 对象数组
5.1 对象数组
-
需求:定义一个长度为3的数组,存储3个Person对象,遍历数组,将三个Person对象中的属性值获取出来
public class Person { private String name; private int age; public Person() { } public Person(String name, int age) { this.name = name; this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } }
public class Demo01ObjectArray { public static void main(String[] args) { // 定义数组 /* Person p = new Person() 1. 定义一个存储int型的数组:int[] 2. 定义一个存储double型的数组:double[] 3. 定义一个存储String型的数组:String[] 2. 定义一个存储Person型的数组:Person[] */ // 定义数组 Person[] arr = new Person[3]; // 创建三个对象 Person p1 = new Person("金莲", 26); Person p2 = new Person("涛哥", 18); Person p3 = new Person("张三", 20); // 将三个对象保存到数组中 arr[0] = p1; arr[1] = p2; arr[2] = p3; /* 遍历 当 i=0,arr[0] = p1对象 当 i=1,arr[1] = p2对象 当 i=2,arr[2] = p3对象 */ for (int i = 0; i < arr.length; i++) { System.out.println(arr[i].getName()+"..."+arr[i].getAge()); } } }
5.2 练习
(1) 定义学生类 Student
声明姓名和成绩成员变量
(2) 测试类ObjectArrayTest的main中创建一个可以装3个学生对象的数组,并且按照学生成绩排序,显示学生信息。
public class Student {
private String name;
private double score;
public Student() {
}
public Student(String name, double score) {
this.name = name;
this.score = score;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public double getScore() {
return score;
}
public void setScore(double score) {
this.score = score;
}
}
public class ObjectArrayTest {
public static void main(String[] args) {
/*
(1) 定义学生类 Student
声明姓名和成绩成员变量
(2) 测试类ObjectArrayTest的main中创建一个可以装3个学生对象的数组,并且按照学生成绩排序,显示学生信息。
*/
Student[] students = new Student[3];
Student stu1 = new Student("小二", 89);
Student stu2 = new Student("小三", 60);
Student stu3 = new Student("小四", 91);
students[0] = stu1;
students[1] = stu2;
students[2] = stu3;
for (int i = 0; i < students.length-1; i++) {
for (int j = 0; j < students.length-1-i; j++) {
// 对学生分数进行冒泡排序
if (students[j].getScore()>students[j+1].getScore()) {
// 零时对象需要声明学生类
Student temp = students[j];
students[j] = students[j+1];
students[j+1]=temp;
}
}
}
for (int i = 0; i < students.length; i++) {
System.out.println(students[i].getName()+"..."+students[i].getScore());
}
}
}
第六章 方法参数
6.1 基本数据类型做方法参数传递
基本类型做方法参数传递,传递的是值,不是变量本身
方法运行:压栈
方法运行完毕:弹栈 → 释放栈内存
public class Demo01Param {
public static void main(String[] args) {
int a = 10;
int b = 20;
method(a, b);
System.out.println("a = " + a); // 10
System.out.println("b = " + b); // 20
}
public static void method(int a, int b) {
a+=10;
b+=20;
System.out.println("a = " + a); // 20
System.out.println("b = " + b); // 40
}
}
6.2 引用数据类型做方法参数传递
引用数据类型做方法传递时,传递的是地址值。
public class Demo02Param {
public static void main(String[] args) {
int[] arr = {10, 20};
method(arr);
System.out.println("arr[0] = " + arr[0]); // 20
System.out.println("arr[1] = " + arr[1]); // 40
}
public static void method(int[] arr) {
arr[0] += 10;
arr[1] += 20;
System.out.println("arr[0] = " + arr[0]); // 20
System.out.println("arr[1] = " + arr[1]); // 40
}
}
6.3 命令行参数(了解)
通过命令行给main方法的形参传递的实参称为命令行参数
public class TestCommandParam {
// 形参 String[] args
public static void main(String[] args) {
for(int i=0;i<args.length;i++) {
System.out.println("第"+(i+1)+"个参数的值是:"+args[i]);
}
}
}
运行命令:
运行之前需要先编译 javac TestCommandParam.java
java TestCommandParam
java TestCommandParam 1 2 3
java TestCommandParam hello atguigu
在IDEA中设置命令行参数:
第七章 其他操作
7.1 快速生成方法
-
初学者要求先定义,再调用;不是初学者,就可以先调用,在定义方法
a. 快捷键:alt + 回车键
public class Demo01Method { /* 快速生成方法的快捷键:alt + 回车(使用时需要将光标移动到先调用的方法中) */ public static void main(String[] args) { // 调用一个无参无返回值方法 method01(); // 调用一个有参无返回值方法 method02(10, 20); // 调用一个无参有返回值方法 int result = method03(); // 调用一个有参有返回值方法 int result1 = method04(10, 20); } private static int method04(int i, int j) { return 0; } private static int method03() { return 0; } private static void method02(int i, int j) { System.out.println(i+j); } private static void method01() { System.out.println("无参无返回值方法"); } }
快速生成方法的快捷键:alt + 回车(使用时需要将光标移动到 紧挨着 先调用的方法后面).
-
快速将一段代码抽取到一个方法中
a. 快捷键:选中要抽取的代码 → ctrl + alt + m
public class Demo02Method { /* 快速将一段代码抽取到一个方法中: 快捷键:选中要抽取的代码 → ctrl + alt + m */ public static void main(String[] args) { int[] arr = {32, 23, 12,56}; int max = 0; for (int i = 0; i < arr.length; i++) { if (arr[i]>max) { max = arr[i]; } } System.out.println("max = " + max); } }
7.2 debug调试
-
概述:调试代码的一种手段
-
作用
a. 能清楚的看到每个变量在代码执行过程中的变化
b. 找错
-
使用
a. 在想要开始debug的那一行左边点一下,出现红色小圆点(断点)
b. 右键 → 点击 debug