第六章 控制语句

1. 选择语句(分支语句)

1.1 if语句

  1. - 对于if语句来说,在任何情况下只能有1个分支执行,不可能存在2个或者更多个分支执行。if语句中只要有1个分支执行了,整个if语句就结束了。
  2. - 当分支当中“java语句;”只有1条,那么大括号{}可以省略
  1. public class IfTest01{
  2. public static void main(String[] args){
  3. // 判断以下程序会出现问题吗?会出什么问题?第几行代码报错????
  4. if(sex)
  5. System.out.println("男");
  6. System.out.println("HelloWorld!"); // 以上的这3行代码没有问题,合乎语法。
  7. /*
  8. else // 这一行编译报错,因为else缺少if
  9. System.out.println("女");
  10. */
  11. }
  12. }

1.2 switch语句

1.2.1 switch语句支持的值有哪些?

  1. - 支持**int**类型以及**String**类型,但一定要注意JDK的版本,JDK8之前不支持String类型,只支持int,**在JDK8之后,switch语句开始支持字符串String类型。**
  2. - switch语句本质上是只支持intString,但是byte,short,char也可以使用在switch语句当中,因为byte short char可以进行自动类型转换。
  3. - 注意:如果分支执行了,但是分支最后没有“break;”,此时会发生case**穿透现象**。
  4. - switch支持byteshortintchar、**enumJDK1.5)**和String(JDK 1.7)等类型(需注意**enum不支持long类型**)

1.2.2 关于case合并的问题

  1. public class SwitchTest01{
  2. public static void main(String[] args){
  3. int num =1;
  4. switch(num){
  5. case 1: case 2: case 3:
  6. System.out.println("星期一");
  7. break;
  8. case 4:
  9. System.out.println("星期二");
  10. break;
  11. case 5:
  12. System.out.println("星期三");
  13. break;
  14. case 6:
  15. System.out.println("星期四");
  16. break;
  17. case 7:
  18. System.out.println("星期五");
  19. break;
  20. case 8:
  21. System.out.println("星期六");
  22. break;
  23. default:
  24. System.out.println("星期日");
  25. }
  26. }
  27. }

2. 循环语句

  • 循环语句的出现就是为了解决代码的复用性。

2.1 for循环

2.1.1 语法机制:

for(初始化表达式; 条件表达式; 更新表达式){
循环体; // 循环体由java语句构成
}

2.1.2 执行原理:

先执行初始化表达式,并且初始化表达式只执行1次,然后判断条件表达式的结果,如果条件表达式结果为true,则执行循环体,循环体结束之后,执行更新表达式;直到更新表达式执行结束之后,再次判断条件时,条件为false,for循环终止。

2.2 while循环

2.2.1 while循环的语法机制以及执行原理

  1. - 语法机制:

while(布尔表达式){
循环体;
}

  1. - 执行原理:

判断布尔表达式的结果,如果为true就执行循环体,循环体结束之后,再次判断布尔表达式的 结果,如果还是true,继续执行循环体,直到布尔表达式结果为false,while循环结束。

2.3 do…while循环

2.3.1 do..while循环语句的执行原理以及语法机制:

  1. - 语法机制:
  2. do {<br /> 循环体;<br /> }<br />while(布尔表达式);<br /> 注意:do..while循环最后的时候别漏掉“分号”
  3. - 执行原理:
  4. 先执行循环体当中的代码,执行一次循环体之后,判断布尔表达式的结果,如果为true 则继续执行循环体,如果为false循环结束。

对于do..while循环来说,循环体至少执行1次。循环体的执行次数是:1~n次。 对于while循环来说,循环体执行次数是:0~n次。

3. 转向语句

3.1 break

  1. - break语句是一个单词成为一个完整的java语句。
  2. - 主要用在switch语句当中,防止case穿透现象,用来终止switch;用在循环语句当中,用来终止循环的执行。
  3. - break;语句只能终止离它最近的循环
  4. - break;语句终止指定的循环:

第一步:你需要给循环起一个名字,例如:
a: for(){
b:for(){

}
}
第二步:终止:break a;

3.2 continue

  1. - 作用是:终止当前"本次"循环,直接进入下一次循环继续执行。
  2. - continue语句后面可以指定循环,步骤和break一致,都是用标记

3.3 return

  1. - 跳出当前方法

第七章 方法与递归

1. 方法

1.1 方法的概念及作用

  1. - 可以完成某个特定功能的并且可以被重复利用的代码片段。在C语言中,方法被称为“函数”。在java中不叫函数,叫做方法。例如当你需要计算不同的值之间的和,就可以用方法来实现,减少代码的重复编写。
  2. - 方法定义:

[修饰符列表] 返回值类型 方法名(形式参数列表){
方法体;
}

注意: [] 符号叫做中括号,以上中括号[]里面的内容表示不是必须的,是可选的。 方法体由Java语句构成。 方法定义之后需要去调用,不调用是不会执行的。

  1. - 如果返回值类型“不是void”,那么你在方法体执行结束的时候必须使用"return 值;"这样的语句来完成“值”的返回。
  2. - 形式参数列表中的每一个参数都是“局部变量”,方法结束之后内存释放。
  3. - 方法定义之后怎么调用:类名.方法名(实际参数列表);
  4. - a()方法调用b()方法的时候,ab方法都在同一个类中,“类名.”可以省略。如果不在同一个类中“类名.”不能省略。
  5. - 方法的返回值可不接收
  6. - 需注意的是型参与实参之间可以自动类型转化,接受的返回值也可以
  7. - return;用来终止离它最近的一个方法。
  8. - 方法的代码片段放在方法区,但是方法执行过程当中需要的内存在栈中。
  9. - 在同一个域当中,"return语句"下面不能再编写其它代码。编写之后编译报错。
  10. - 为什么方法结束后,方法内的形参变量,内存会释放:因为不释放的话,方法在调用,变量名重名,导致重复声明。
  1. public class MethodTest06{
  2. //main方法的返回值类型是void,表示没有返回值。
  3. public static void main(String[] args){
  4. for(int i = 0; i < 10; i++){
  5. if(i == 5){
  6. //break; // 终止for循环
  7. return; // 终止当前的方法,和break;不是一个级别的。
  8. //错误: 不兼容的类型: 意外的返回值
  9. //return 10;
  10. }
  11. System.out.println("i = " + i);
  12. }
  13. System.out.println("Hello World!");
  14. }
  15. public static int m(){
  16. boolean flag = true; //编译器不负责运行程序,编译器只讲道理。
  17. // 对于编译器来说,编译器只知道flag变量是boolean类型
  18. // 编译器会认为flag有可能是false,有可能是true
  19. if(flag){
  20. // 编译器觉得:以下这行代码可能会执行,当然也可能不会执行
  21. // 编译器为了确保程序不出现任何异常,所以编译器说:缺少返回语句
  22. return 1;
  23. }
  24. }
  25. }

1.2 语法机制

栈内存结构:
image.png

  1. // 局部变量:只在方法体中有效,方法结束之后,局部变量的内存就释放了。
  2. // JVM三块主要的内存:栈内存、堆内存、方法区内存。
  3. // 方法区最先有数据:方法区中放代码片段。存放class字节码。
  4. // 堆内存:后面讲。
  5. // 栈内存:方法调用的时候,该方法需要的内存空间在栈中分配。
  6. // 方法不调用是不会在栈中分配空间的。
  7. // 方法只有在调用的时候才会在栈中分配空间,并且调用时就是压栈。
  8. // 方法执行结束之后,该方法所需要的空间就会释放,此时发生弹栈动作。
  9. // 方法调用叫做:压栈。分配空间
  10. // 方法结束叫做:弹栈。释放空间
  11. // 栈中存储什么?方法运行过程中需要的内存,以及栈中会存储方法的局部变量。
  12. public class MethodTest08{
  13. //主方法,入口
  14. public static void main(String[] args){
  15. //int a = 100;
  16. // 这个赋值原理是:将a变量中保存的100这个数字复制一份传给b变量。
  17. // 所以a和b是两个不同的内存空间,是两个局部变量。
  18. //int b = a;
  19. System.out.println("main begin");
  20. int x = 100;
  21. m1(x);
  22. System.out.println("main over");
  23. }
  24. public static void m1(int i){ // i是局部变量
  25. System.out.println("m1 begin");
  26. m2(i);
  27. System.out.println("m1 over");
  28. }
  29. public static void m2(int i){
  30. System.out.println("m2 begin");
  31. m3(i);
  32. System.out.println("m2 over");
  33. }
  34. public static void m3(int i){
  35. System.out.println("m3 begin");
  36. System.out.println(i);
  37. System.out.println("m3 over");
  38. }
  39. }

上述代码运行图解:
image.png

图中类似于“X”型的,代表弹栈

2. 方法重载

2.1 方法重载的优点

  1. - 方法重载最集中的体现就是,我我们常用的System.out.print(),这条输出语句,我们可以输出多种类型的字面量,其优点便是,只需记忆一个方法名就可以实现输出多种类型,代码也更加整齐美观。
  2. - java语言中,是怎么进行区分这些重载的方法:

首先java编译器会通过方法名进行区分,如果方法名相同的情况下,编译器会通过方法的参数 类型进行方法的区分。

2.2 方法重载的条件

  1. - 在同一个类当中
  2. - 方法名相同
  3. - 参数列表不同(即个数、类型、顺序不同)
  4. - 方法重载和方法的“返回值类型”无关。
  5. - 方法重载和方法的“修饰符列表”无关。
  1. public class OverloadTest03{
  2. public static void main(String[] args){
  3. }
  4. //分析:以下两个方法有没有发生重载?
  5. // 编译器报错了,不是重载,这是重复了:呵呵。
  6. /*
  7. public static void m4(int a, int b){
  8. }
  9. public static void m4(int x, int y){
  10. }
  11. */
  12. // 这两个方法有没有发生重载呢?
  13. // 这不是重载,这是方法重复了。
  14. /*
  15. public static int m5(){
  16. return 1;
  17. }
  18. public static double m5(){
  19. return 1.0;
  20. }
  21. */
  22. //这两个方法重载了吗?
  23. // 这个方法没有修饰符列表
  24. // 这不是重载,是重复了。
  25. /*
  26. void m6(){
  27. }
  28. // 这个有修饰符列表
  29. public static void m6(){
  30. }
  31. */
  32. }

3. 方法递归

3.1 方法递归是什么

方法自己调用自己,这就是方法递归。

3.2 StackOverflowError

当递归时程序没有结束条件,一定会发生:栈内存溢出错误:StackOverflowError

  1. public class RecursionTest01{
  2. // 入口
  3. public static void main(String[] args){
  4. doSome();
  5. }
  6. public static void doSome(){
  7. System.out.println("doSome begin");
  8. // 调用方法:doSome()既然是一个方法,那么doSome方法可以调用吗?当然可以。
  9. // 目前这个递归是没有结束条件的,会出现什么问题?
  10. doSome();
  11. // 这行代码永远执行不到。
  12. System.out.println("doSome over");
  13. }

StackOverflowError图解:

image.png

递归假设是有结束条件的,就一定不会发生栈内存溢出错误吗?

假设这个结束条件是对的,是合法的,递归有的时候也会出现栈内存溢出错误。
因为有可能递归的太深,栈内存不够了。因为一直在压栈。
在实际的开发中,不建议轻易的选择递归,能用for循环while循环代替的,尽量使用循环来做;因 为循环的效率高,耗费的内存少;递归耗费的内存比较大。

3.3 如何解决StackOverflowError

第一步:
先检查递归的结束条件对不对。如果递归结束条件不对,必须对条件进一步修改,直到正确为 止。
第二步:假设递归条件没问题,怎么办?
这个时候需要手动的调整JVM的栈内存初始化大小。
可以将栈内存的空间调大点。(可以调整大一些。)
第三步:调整了大小,如果运行时还是出现这个错误,没办法,只能继续扩大栈的内存大小。

  1. java -X 这个可以查看调整堆栈大小的参数

第八章 面向对象初步

1. 面向过程和面向对象的区别

1.1 面向过程的缺点(耦合度高,扩展力差。)

面向过程最主要是每一步与每一步的因果关系,其中A步骤因果关系到B步骤,A和B联合起来形成一个子模块,子模块和子模块之间又因为因果关系结合在一起,假设其中任何一个因果关系出现问题(错误),此时整个系统的运转都会出现问题。

1.2 面向过程的优点(快速开发)

对于小型项目(功能),采用面向过程的方式进行开发,效率较高。不需要前期进行对象的提取,模型的建立,采用面向过程方式可以直接开始干活。一上来直接写代码,编写因果关系。从而实现功能。

1.3 面向对象

面向对象就是将现实世界分割成不同的单元,然后每一个单元都实现成对象,然后给一个环境驱动一下,让各个对象之间协作起来形成一个系统。

1.4 OOA、OOD、OOP

OOA:面向对象分析
OOD:面向对象设计
OOP:面向对象编程
整个软件开发的过程,都是采用OO进行贯穿的。
实现一个软件的过程:
分析(A) —> 设计(D) —> 编程(P)

2. 类和对象

类实际上在现实世界当中是不存在的,是一个抽象的概念;对象是实际存在的个体。(真实存在的个体)就比如,汽车是一个类,你买了一辆某个牌子啥型号的一辆车,这个车是一个真实的个体,这个就是对象。

2.1 类的定义

[修饰符列表] class 类名 {
//类体 = 属性 + 方法
// 属性在代码上以“变量”的形式存在(描述状态)
// 方法描述动作/行为
}

当我们把两个类写到两个文件中,在一个类中定义主方法创建另一个类的对象,当编译含有主方法的类时,另一个类也会编译。

2.2 对象的创建

  1. - --【实例化】--> 对象(实例)
  2. - 类是模板,通过一个类,是可以创建N多个对象的;new是一个运算符。专门负责对象的创建。
  3. - 创建对象语法:类名 变量名 = new 类名();

image.png

2.3 对象和引用的区别

对象是通过new出来的,在堆内存中存储。
引用是:但凡是变量,并且该变量中保存了内存地址指向了堆内存当中的对象的。

2.4 成员变量默认值

变量必须先声明,再赋值才能访问。

注意:对于成员变量来说,没有手动赋值时,系统默认赋值。 赋的值都是默认值,那么默认值是什么?

类型 默认值
——————————————————-
byte 0
short 0
int 0
long 0L
float 0.0F
double 0.0
boolean false
char \u0000
引用数据类型 null

  1. public class StudentTest{
  2. public static void main(String[] args){
  3. //局部变量
  4. //错误: 可能尚未初始化变量k
  5. /*
  6. int k;
  7. System.out.println(k);
  8. */
  9. //访问学生姓名可以直接通过类名吗?
  10. // 学生姓名是一个实例变量。实例变量是对象级别的变量。
  11. // 是不是应该先有对象才能说姓名的事儿。
  12. // 不能通过“类名”来直接访问“实例变量”。
  13. //System.out.println(Student.name);
  14. // i属于局部变量吗?当然是。
  15. // 局部变量存储在栈内存当中。(栈主要存储局部变量。)
  16. //int i = 100;
  17. // 创建学生对象1
  18. // s1属于局部变量吗?当然是。
  19. // s1这个局部变量叫做引用
  20. Student s1 = new Student();
  21. // 怎么访问实例变量?
  22. // 语法:引用.实例变量名
  23. System.out.println(s1.no);
  24. System.out.println(s1.name);
  25. System.out.println(s1.age);
  26. System.out.println(s1.sex);
  27. System.out.println(s1.addr);
  28. System.out.println("-----------------------------");
  29. // 创建学生对象2
  30. // s2也是局部变量。
  31. // s2也叫做引用。
  32. Student s2 = new Student();
  33. System.out.println(s2.no);
  34. System.out.println(s2.name);
  35. System.out.println(s2.age);
  36. System.out.println(s2.sex);
  37. System.out.println(s2.addr);
  38. // 程序执行到此处我可以修改s1这个学生的学号吗?
  39. // 通过“=”赋值的方式将内存中实例变量的值修改一下。
  40. s1.no = 110;
  41. s1.name = "张三";
  42. s1.age = 20;
  43. s1.sex = true;
  44. s1.addr = "深圳宝安区";
  45. System.out.println("学号=" + s1.no);
  46. System.out.println("姓名=" + s1.name);
  47. System.out.println("年龄=" + s1.age);
  48. System.out.println("性别=" + s1.sex);
  49. System.out.println("住址=" + s1.addr);
  50. // 再次赋值
  51. s1.addr = "北京大兴区";
  52. System.out.println("住址:" + s1.addr);
  53. }
  54. public static void method(){
  55. // i s1 s2都是main方法中的局部变量,在这里是无法访问的。
  56. /*
  57. System.out.println(i);
  58. System.out.println(s1);
  59. System.out.println(s2);
  60. */
  61. }
  62. }
  1. public class Student {
  2. public int no;
  3. public String name;
  4. public int age;
  5. public boolean sex;
  6. public String addr;
  7. }

上述代码图解:

image.png

2.5 NullPointerException

关于垃圾回收器:
GC在java语言中,垃圾回收器主要针对的是堆内存。当一个java对象没有任何引用指向该对象的时 候,GC会考虑将该垃圾数据释放回收掉。
出现空指针异常的前提条件:
“空引用”访问实例【对象相关】相关的数据时,都会出现空指针异常。

  1. public class NullPointerTest{
  2. public static void main(String[] args){
  3. // 创建客户对象
  4. Customer c = new Customer();
  5. // 访问这个客户的id
  6. System.out.println(c.id); // 0
  7. // 重新给id赋值
  8. c.id = 9521; // 终身代号
  9. System.out.println("客户的id是=" + c.id);
  10. c = null;
  11. // NullPointerException
  12. // 编译器没问题,因为编译器只检查语法,编译器发现c是Customer类型,
  13. // Customer类型中有id属性,所以可以:c.id。语法过了。
  14. // 但是运行的时候需要对象的存在,但是对象没了,尴尬了,就只能出现一个异常。
  15. System.out.println(c.id);
  16. }
  17. }
  18. class Customer{
  19. // 客户id
  20. int id; // 成员变量中的实例变量,应该先创建对象,然后通过“引用.”的方式访问。
  21. }

2.6 进一步理解访问类中属性

引用和对象怎么区分:
“引用”是啥?是存储对象内存地址的一个变量。
“对象”是啥?堆里new出来的。

  1. // 住址类
  2. public class Address{
  3. // 一个家庭住址有3个属性。
  4. // 城市
  5. String city; // 实例变量
  6. // 街道
  7. String street;
  8. // 邮编
  9. String zipcode;
  10. }
  1. public class User{
  2. // 类=属性+方法
  3. // 以下3个都是属性,都是实例变量。(对象变量。)
  4. // 用户id
  5. // int是一种基本数据类型
  6. int id; // 实例变量
  7. // 用户名
  8. // String是一种引用数据类型
  9. String username; // 实例变量
  10. // 家庭住址
  11. // Address是一种引用数据类型
  12. // addr是成员变量并且还是一个实例变量
  13. // addr是否是一个引用呢?是。addr是一个引用。
  14. Address addr;
  15. }
  16. // 实例变量都存储在哪里?
  17. // 实例变量都在堆内存的对象内部。
  18. // 方法体外,类体内定义的变量叫做:成员变量。
  1. public class Test{
  2. public static void main(String[] args){
  3. //报错了。id是实例变量,必须先创建对象,通过“引用.”的方式访问。
  4. /*
  5. User u = new User();
  6. u是引用。
  7. */
  8. //System.out.println(User.id);
  9. /*
  10. int i = 100;
  11. int j = i; // 原理:会将i中保存的100复制一份,传给j变量。
  12. */
  13. // 家庭住址对象
  14. Address a = new Address();
  15. a.city = "北京";
  16. a.street = "大兴区";
  17. a.zipcode = "121221";
  18. // 用户对象
  19. User u = new User();
  20. System.out.println(u.id); // 0
  21. System.out.println(u.username); // null
  22. System.out.println(u.addr); // null
  23. u.id = 11111;
  24. u.username = "zhangsan";
  25. u.addr = a;
  26. // 思考一个问题:
  27. // 我想直到zhangsan他是哪个城市的,代码应该怎么写?
  28. System.out.println(u.username + "是"+u.addr.city+"城市的!");
  29. // u.addr.city 这行代码可否拆分呢?u.addr.city 节省变量。
  30. // 拆分成以下代码和以上效果完全相同,原理完全相同,不同的是以下代码多了两个变量。
  31. Address ad = u.addr;
  32. String zhuZhi = ad.city;
  33. System.out.println(zhuZhi);
  34. //-----------------------是否理解以下代码---------------------------
  35. int x = 100;
  36. // = 代表赋值运算,“赋值”中有一个“值”
  37. // x变量中的值是100. 将100复制一份给y
  38. // 表示:将x变量中保存的值100复制一份给y
  39. int y = x;
  40. //-----------------------是否理解以下代码---------------------------
  41. Address k = new Address(); // Address k = 0x1111;
  42. Address m = k; // 这里表示将k变量中保存的0x1111复制了一份传给了m变量。
  43. }
  44. }

2.7 方法调用时参数的传递问题

java中关于方法调用时参数传递实际上只有一个规则:
不管你是基本数据类型,还是引用数据类型,实际上在传递的时候都是
将变量中保存的那个“值”复制一份,传过去。
int x = 1;
int y = x; 把x中保存1复制一份传给y
x和y都是两个局部变量。
Person p1 = 0x1234;
Person p2 = p1; 把p1中保存的0x1234复制一份传给p2
p1和p2都是两个局部变量。

实际上,在java语言中,方法调用时参数传递,和类型无关,都是将变量中保存 的那个“值”传过去,这个“值”可能是一个数字100,也可能是一个java对象的内存 地址:0x1234 记住这句话:不管是哪一种数据类型的传递,都是将“变量中保存的那个值复制一份传递过去。”

  1. public class Test2{
  2. public static void main(String[] args){
  3. Person p = new Person();
  4. p.age = 10;
  5. add(p);
  6. System.out.println("main--->" + p.age); //11
  7. }
  8. // 方法的参数可以是基本数据类型,也可以是引用数据类型,只要是合法的数据类型就行。
  9. public static void add(Person p){ // p是add方法的局部变量。
  10. p.age++;
  11. System.out.println("add--->" + p.age); //11
  12. }
  13. }
  14. class Person{
  15. // 年龄属性,成员变量中的实例变量。
  16. int age;
  17. }

3. 构造方法

3.1 构造方法概念及作用

  1. - 构造方法是用来创建对象,并且同时给对象的属性赋值。(注意:实例变量没有手动赋值的时候,系统会赋默认值。)
  2. - 当一个类没有提供任何构造方法,系统会默认提供一个无参数的构造方法。(而这个构造方法被称为缺省构造器。)
  3. - 构造方法支持方法重载
  4. - 使用new运算符来调用构造方法:new 构造方法名(实际参数列表);
  5. - 构造方法的语法结构:
  6. [修饰符列表] 构造方法名(形式参数列表){<br /> 构造方法体;<br /> 通常在构造方法体当中给属性赋值,完成属性的初始化。<br /> }

注意: 构造方法名和类名必须一致。 构造方法不需要指定返回值类型,也不能写void,写上void 表示普通方法,就不是构造方法了。