第六章 控制语句
1. 选择语句(分支语句)
1.1 if语句
- 对于if语句来说,在任何情况下只能有1个分支执行,不可能存在2个或者更多个分支执行。if语句中只要有1个分支执行了,整个if语句就结束了。
- 当分支当中“java语句;”只有1条,那么大括号{}可以省略
public class IfTest01{
public static void main(String[] args){
// 判断以下程序会出现问题吗?会出什么问题?第几行代码报错????
if(sex)
System.out.println("男");
System.out.println("HelloWorld!"); // 以上的这3行代码没有问题,合乎语法。
/*
else // 这一行编译报错,因为else缺少if
System.out.println("女");
*/
}
}
1.2 switch语句
1.2.1 switch语句支持的值有哪些?
- 支持**int**类型以及**String**类型,但一定要注意JDK的版本,JDK8之前不支持String类型,只支持int,**在JDK8之后,switch语句开始支持字符串String类型。**
- switch语句本质上是只支持int和String,但是byte,short,char也可以使用在switch语句当中,因为byte short char可以进行自动类型转换。
- 注意:如果分支执行了,但是分支最后没有“break;”,此时会发生case**穿透现象**。
- switch支持byte、short、int、char、**enum(JDK1.5)**和String(JDK 1.7)等类型(需注意**enum不支持long类型**)
1.2.2 关于case合并的问题
public class SwitchTest01{
public static void main(String[] args){
int num =1;
switch(num){
case 1: case 2: case 3:
System.out.println("星期一");
break;
case 4:
System.out.println("星期二");
break;
case 5:
System.out.println("星期三");
break;
case 6:
System.out.println("星期四");
break;
case 7:
System.out.println("星期五");
break;
case 8:
System.out.println("星期六");
break;
default:
System.out.println("星期日");
}
}
}
2. 循环语句
- 循环语句的出现就是为了解决代码的复用性。
2.1 for循环
2.1.1 语法机制:
for(初始化表达式; 条件表达式; 更新表达式){
循环体; // 循环体由java语句构成
}
2.1.2 执行原理:
先执行初始化表达式,并且初始化表达式只执行1次,然后判断条件表达式的结果,如果条件表达式结果为true,则执行循环体,循环体结束之后,执行更新表达式;直到更新表达式执行结束之后,再次判断条件时,条件为false,for循环终止。
2.2 while循环
2.2.1 while循环的语法机制以及执行原理
- 语法机制:
while(布尔表达式){
循环体;
}
- 执行原理:
判断布尔表达式的结果,如果为true就执行循环体,循环体结束之后,再次判断布尔表达式的 结果,如果还是true,继续执行循环体,直到布尔表达式结果为false,while循环结束。
2.3 do…while循环
2.3.1 do..while循环语句的执行原理以及语法机制:
- 语法机制:
do {<br /> 循环体;<br /> }<br />while(布尔表达式);<br /> 注意:do..while循环最后的时候别漏掉“分号”
- 执行原理:
先执行循环体当中的代码,执行一次循环体之后,判断布尔表达式的结果,如果为true, 则继续执行循环体,如果为false循环结束。
对于do..while循环来说,循环体至少执行1次。循环体的执行次数是:1~n次。 对于while循环来说,循环体执行次数是:0~n次。
3. 转向语句
3.1 break
- break语句是一个单词成为一个完整的java语句。
- 主要用在switch语句当中,防止case穿透现象,用来终止switch;用在循环语句当中,用来终止循环的执行。
- break;语句只能终止离它最近的循环
- break;语句终止指定的循环:
第一步:你需要给循环起一个名字,例如:
a: for(){
b:for(){
}
}
第二步:终止:break a;
3.2 continue
- 作用是:终止当前"本次"循环,直接进入下一次循环继续执行。
- continue语句后面可以指定循环,步骤和break一致,都是用标记
3.3 return
- 跳出当前方法
第七章 方法与递归
1. 方法
1.1 方法的概念及作用
- 可以完成某个特定功能的并且可以被重复利用的代码片段。在C语言中,方法被称为“函数”。在java中不叫函数,叫做方法。例如当你需要计算不同的值之间的和,就可以用方法来实现,减少代码的重复编写。
- 方法定义:
[修饰符列表] 返回值类型 方法名(形式参数列表){
方法体;
}
注意: [] 符号叫做中括号,以上中括号[]里面的内容表示不是必须的,是可选的。 方法体由Java语句构成。 方法定义之后需要去调用,不调用是不会执行的。
- 如果返回值类型“不是void”,那么你在方法体执行结束的时候必须使用"return 值;"这样的语句来完成“值”的返回。
- 形式参数列表中的每一个参数都是“局部变量”,方法结束之后内存释放。
- 方法定义之后怎么调用:类名.方法名(实际参数列表);
- a()方法调用b()方法的时候,a和b方法都在同一个类中,“类名.”可以省略。如果不在同一个类中“类名.”不能省略。
- 方法的返回值可不接收
- 需注意的是型参与实参之间可以自动类型转化,接受的返回值也可以
- return;用来终止离它最近的一个方法。
- 方法的代码片段放在方法区,但是方法执行过程当中需要的内存在栈中。
- 在同一个域当中,"return语句"下面不能再编写其它代码。编写之后编译报错。
- 为什么方法结束后,方法内的形参变量,内存会释放:因为不释放的话,方法在调用,变量名重名,导致重复声明。
public class MethodTest06{
//main方法的返回值类型是void,表示没有返回值。
public static void main(String[] args){
for(int i = 0; i < 10; i++){
if(i == 5){
//break; // 终止for循环
return; // 终止当前的方法,和break;不是一个级别的。
//错误: 不兼容的类型: 意外的返回值
//return 10;
}
System.out.println("i = " + i);
}
System.out.println("Hello World!");
}
public static int m(){
boolean flag = true; //编译器不负责运行程序,编译器只讲道理。
// 对于编译器来说,编译器只知道flag变量是boolean类型
// 编译器会认为flag有可能是false,有可能是true
if(flag){
// 编译器觉得:以下这行代码可能会执行,当然也可能不会执行
// 编译器为了确保程序不出现任何异常,所以编译器说:缺少返回语句
return 1;
}
}
}
1.2 语法机制
栈内存结构:
// 局部变量:只在方法体中有效,方法结束之后,局部变量的内存就释放了。
// JVM三块主要的内存:栈内存、堆内存、方法区内存。
// 方法区最先有数据:方法区中放代码片段。存放class字节码。
// 堆内存:后面讲。
// 栈内存:方法调用的时候,该方法需要的内存空间在栈中分配。
// 方法不调用是不会在栈中分配空间的。
// 方法只有在调用的时候才会在栈中分配空间,并且调用时就是压栈。
// 方法执行结束之后,该方法所需要的空间就会释放,此时发生弹栈动作。
// 方法调用叫做:压栈。分配空间
// 方法结束叫做:弹栈。释放空间
// 栈中存储什么?方法运行过程中需要的内存,以及栈中会存储方法的局部变量。
public class MethodTest08{
//主方法,入口
public static void main(String[] args){
//int a = 100;
// 这个赋值原理是:将a变量中保存的100这个数字复制一份传给b变量。
// 所以a和b是两个不同的内存空间,是两个局部变量。
//int b = a;
System.out.println("main begin");
int x = 100;
m1(x);
System.out.println("main over");
}
public static void m1(int i){ // i是局部变量
System.out.println("m1 begin");
m2(i);
System.out.println("m1 over");
}
public static void m2(int i){
System.out.println("m2 begin");
m3(i);
System.out.println("m2 over");
}
public static void m3(int i){
System.out.println("m3 begin");
System.out.println(i);
System.out.println("m3 over");
}
}
上述代码运行图解:
图中类似于“X”型的,代表弹栈
2. 方法重载
2.1 方法重载的优点
- 方法重载最集中的体现就是,我我们常用的System.out.print(),这条输出语句,我们可以输出多种类型的字面量,其优点便是,只需记忆一个方法名就可以实现输出多种类型,代码也更加整齐美观。
- 在java语言中,是怎么进行区分这些重载的方法:
首先java编译器会通过方法名进行区分,如果方法名相同的情况下,编译器会通过方法的参数 类型进行方法的区分。
2.2 方法重载的条件
- 在同一个类当中
- 方法名相同
- 参数列表不同(即个数、类型、顺序不同)
- 方法重载和方法的“返回值类型”无关。
- 方法重载和方法的“修饰符列表”无关。
public class OverloadTest03{
public static void main(String[] args){
}
//分析:以下两个方法有没有发生重载?
// 编译器报错了,不是重载,这是重复了:呵呵。
/*
public static void m4(int a, int b){
}
public static void m4(int x, int y){
}
*/
// 这两个方法有没有发生重载呢?
// 这不是重载,这是方法重复了。
/*
public static int m5(){
return 1;
}
public static double m5(){
return 1.0;
}
*/
//这两个方法重载了吗?
// 这个方法没有修饰符列表
// 这不是重载,是重复了。
/*
void m6(){
}
// 这个有修饰符列表
public static void m6(){
}
*/
}
3. 方法递归
3.1 方法递归是什么
方法自己调用自己,这就是方法递归。
3.2 StackOverflowError
当递归时程序没有结束条件,一定会发生:栈内存溢出错误:StackOverflowError
public class RecursionTest01{
// 入口
public static void main(String[] args){
doSome();
}
public static void doSome(){
System.out.println("doSome begin");
// 调用方法:doSome()既然是一个方法,那么doSome方法可以调用吗?当然可以。
// 目前这个递归是没有结束条件的,会出现什么问题?
doSome();
// 这行代码永远执行不到。
System.out.println("doSome over");
}
StackOverflowError图解:
递归假设是有结束条件的,就一定不会发生栈内存溢出错误吗?
假设这个结束条件是对的,是合法的,递归有的时候也会出现栈内存溢出错误。
因为有可能递归的太深,栈内存不够了。因为一直在压栈。
在实际的开发中,不建议轻易的选择递归,能用for循环while循环代替的,尽量使用循环来做;因 为循环的效率高,耗费的内存少;递归耗费的内存比较大。
3.3 如何解决StackOverflowError
第一步:
先检查递归的结束条件对不对。如果递归结束条件不对,必须对条件进一步修改,直到正确为 止。
第二步:假设递归条件没问题,怎么办?
这个时候需要手动的调整JVM的栈内存初始化大小。
可以将栈内存的空间调大点。(可以调整大一些。)
第三步:调整了大小,如果运行时还是出现这个错误,没办法,只能继续扩大栈的内存大小。
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 对象的创建
- 类 --【实例化】--> 对象(实例)
- 类是模板,通过一个类,是可以创建N多个对象的;new是一个运算符。专门负责对象的创建。
- 创建对象语法:类名 变量名 = new 类名();
2.3 对象和引用的区别
对象是通过new出来的,在堆内存中存储。
引用是:但凡是变量,并且该变量中保存了内存地址指向了堆内存当中的对象的。
2.4 成员变量默认值
变量必须先声明,再赋值才能访问。
注意:对于成员变量来说,没有手动赋值时,系统默认赋值。 赋的值都是默认值,那么默认值是什么?
类型 默认值
——————————————————-
byte 0
short 0
int 0
long 0L
float 0.0F
double 0.0
boolean false
char \u0000
引用数据类型 null
public class StudentTest{
public static void main(String[] args){
//局部变量
//错误: 可能尚未初始化变量k
/*
int k;
System.out.println(k);
*/
//访问学生姓名可以直接通过类名吗?
// 学生姓名是一个实例变量。实例变量是对象级别的变量。
// 是不是应该先有对象才能说姓名的事儿。
// 不能通过“类名”来直接访问“实例变量”。
//System.out.println(Student.name);
// i属于局部变量吗?当然是。
// 局部变量存储在栈内存当中。(栈主要存储局部变量。)
//int i = 100;
// 创建学生对象1
// s1属于局部变量吗?当然是。
// s1这个局部变量叫做引用
Student s1 = new Student();
// 怎么访问实例变量?
// 语法:引用.实例变量名
System.out.println(s1.no);
System.out.println(s1.name);
System.out.println(s1.age);
System.out.println(s1.sex);
System.out.println(s1.addr);
System.out.println("-----------------------------");
// 创建学生对象2
// s2也是局部变量。
// s2也叫做引用。
Student s2 = new Student();
System.out.println(s2.no);
System.out.println(s2.name);
System.out.println(s2.age);
System.out.println(s2.sex);
System.out.println(s2.addr);
// 程序执行到此处我可以修改s1这个学生的学号吗?
// 通过“=”赋值的方式将内存中实例变量的值修改一下。
s1.no = 110;
s1.name = "张三";
s1.age = 20;
s1.sex = true;
s1.addr = "深圳宝安区";
System.out.println("学号=" + s1.no);
System.out.println("姓名=" + s1.name);
System.out.println("年龄=" + s1.age);
System.out.println("性别=" + s1.sex);
System.out.println("住址=" + s1.addr);
// 再次赋值
s1.addr = "北京大兴区";
System.out.println("住址:" + s1.addr);
}
public static void method(){
// i s1 s2都是main方法中的局部变量,在这里是无法访问的。
/*
System.out.println(i);
System.out.println(s1);
System.out.println(s2);
*/
}
}
public class Student {
public int no;
public String name;
public int age;
public boolean sex;
public String addr;
}
上述代码图解:
2.5 NullPointerException
关于垃圾回收器:
GC在java语言中,垃圾回收器主要针对的是堆内存。当一个java对象没有任何引用指向该对象的时 候,GC会考虑将该垃圾数据释放回收掉。
出现空指针异常的前提条件:
“空引用”访问实例【对象相关】相关的数据时,都会出现空指针异常。
public class NullPointerTest{
public static void main(String[] args){
// 创建客户对象
Customer c = new Customer();
// 访问这个客户的id
System.out.println(c.id); // 0
// 重新给id赋值
c.id = 9521; // 终身代号
System.out.println("客户的id是=" + c.id);
c = null;
// NullPointerException
// 编译器没问题,因为编译器只检查语法,编译器发现c是Customer类型,
// Customer类型中有id属性,所以可以:c.id。语法过了。
// 但是运行的时候需要对象的存在,但是对象没了,尴尬了,就只能出现一个异常。
System.out.println(c.id);
}
}
class Customer{
// 客户id
int id; // 成员变量中的实例变量,应该先创建对象,然后通过“引用.”的方式访问。
}
2.6 进一步理解访问类中属性
引用和对象怎么区分:
“引用”是啥?是存储对象内存地址的一个变量。
“对象”是啥?堆里new出来的。
// 住址类
public class Address{
// 一个家庭住址有3个属性。
// 城市
String city; // 实例变量
// 街道
String street;
// 邮编
String zipcode;
}
public class User{
// 类=属性+方法
// 以下3个都是属性,都是实例变量。(对象变量。)
// 用户id
// int是一种基本数据类型
int id; // 实例变量
// 用户名
// String是一种引用数据类型
String username; // 实例变量
// 家庭住址
// Address是一种引用数据类型
// addr是成员变量并且还是一个实例变量
// addr是否是一个引用呢?是。addr是一个引用。
Address addr;
}
// 实例变量都存储在哪里?
// 实例变量都在堆内存的对象内部。
// 方法体外,类体内定义的变量叫做:成员变量。
public class Test{
public static void main(String[] args){
//报错了。id是实例变量,必须先创建对象,通过“引用.”的方式访问。
/*
User u = new User();
u是引用。
*/
//System.out.println(User.id);
/*
int i = 100;
int j = i; // 原理:会将i中保存的100复制一份,传给j变量。
*/
// 家庭住址对象
Address a = new Address();
a.city = "北京";
a.street = "大兴区";
a.zipcode = "121221";
// 用户对象
User u = new User();
System.out.println(u.id); // 0
System.out.println(u.username); // null
System.out.println(u.addr); // null
u.id = 11111;
u.username = "zhangsan";
u.addr = a;
// 思考一个问题:
// 我想直到zhangsan他是哪个城市的,代码应该怎么写?
System.out.println(u.username + "是"+u.addr.city+"城市的!");
// u.addr.city 这行代码可否拆分呢?u.addr.city 节省变量。
// 拆分成以下代码和以上效果完全相同,原理完全相同,不同的是以下代码多了两个变量。
Address ad = u.addr;
String zhuZhi = ad.city;
System.out.println(zhuZhi);
//-----------------------是否理解以下代码---------------------------
int x = 100;
// = 代表赋值运算,“赋值”中有一个“值”
// x变量中的值是100. 将100复制一份给y
// 表示:将x变量中保存的值100复制一份给y
int y = x;
//-----------------------是否理解以下代码---------------------------
Address k = new Address(); // Address k = 0x1111;
Address m = k; // 这里表示将k变量中保存的0x1111复制了一份传给了m变量。
}
}
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 记住这句话:不管是哪一种数据类型的传递,都是将“变量中保存的那个值复制一份传递过去。”
public class Test2{
public static void main(String[] args){
Person p = new Person();
p.age = 10;
add(p);
System.out.println("main--->" + p.age); //11
}
// 方法的参数可以是基本数据类型,也可以是引用数据类型,只要是合法的数据类型就行。
public static void add(Person p){ // p是add方法的局部变量。
p.age++;
System.out.println("add--->" + p.age); //11
}
}
class Person{
// 年龄属性,成员变量中的实例变量。
int age;
}
3. 构造方法
3.1 构造方法概念及作用
- 构造方法是用来创建对象,并且同时给对象的属性赋值。(注意:实例变量没有手动赋值的时候,系统会赋默认值。)
- 当一个类没有提供任何构造方法,系统会默认提供一个无参数的构造方法。(而这个构造方法被称为缺省构造器。)
- 构造方法支持方法重载
- 使用new运算符来调用构造方法:new 构造方法名(实际参数列表);
- 构造方法的语法结构:
[修饰符列表] 构造方法名(形式参数列表){<br /> 构造方法体;<br /> 通常在构造方法体当中给属性赋值,完成属性的初始化。<br /> }
注意: 构造方法名和类名必须一致。 构造方法不需要指定返回值类型,也不能写void,写上void 表示普通方法,就不是构造方法了。