学习面向对象内容的三条主线
- Java类及类的成员:(重点)属性、方法、构造器;(熟悉)代码块、内部类
- 面向对象的特征:封装、继承、多态、(抽象)
- 其他关键字的使用:this、super、package、import、static、final、interface、abstract等
面向对象编程概述
程序设计的思路
面向对象,是软件开发中的一类编程风格、开发范式。除了 面向对象 ,还有 面向过程 、 指令式编程 和 函数式编程 。在所有的编程范式中,我们接触最多的还是面向过程和面向对象两种。
-
面向过程的程序设计思想(Process-Oriented Programming),简称 POP
关注的焦点是 过程 :过程就是操作数据的步骤。如果某个过程的实现代码重复出现,那么就可以 把这个过程抽取为一个 函数 。这样就可以大大简化冗余代码,便于维护。
典型的语言:C语言
代码结构:以函数
为组织单位。
是一种“ 执行者思维 ”,适合解决简单问题。扩展能力差、后期维护难度较大。
-
面向对象的程序设计思想( Object Oriented Programming),简称 OOP
关注的焦点是 类 :在计算机程序设计过程中,参照现实中事物,将事物的属性特征、行为特征抽 象出来,用类来表示。
典型的语言:Java、C#、C++、Python、Ruby和PHP等
代码结构:以 类
为组织单位。每种事物都具备自己的 属性 和 行为/功能 。
是一种“ 设计者思维 ”,适合解决复杂问题。代码扩展性强、可维护性高。
造车太复杂,需要 很多协作
才能完成。此时我们思考的是“ 车怎么设计? ”,而不是“怎么按特定步骤造 车的问题”。这就是思维方式的转变,前者就是面向对象思想。所以,面向对象(Oriented-Object)思想更 契合人的思维模式。车由很多结构组成
我们找轮胎厂完成制造轮胎的步骤,发动机厂完成制造发动机的步骤,…;这样,大家可以同时进行车 的制造,最终进行组装,大大提高了效率。但是,具体到轮胎厂的一个流水线操作,仍然是有步骤的, 还是离不开面向过程思维! 因此,面向对象可以帮助我们从宏观上把握、从整体上分析整个系统。 但是,具体到实现部分的微观操 作(就是一个个方法),仍然需要面向过程的思路去处理。
人把大象装进冰箱
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
|
面向过程
1.打开冰箱
2.把大象装进冰箱
3.把冰箱门关住
面向对象
人{
打开(冰箱){
冰箱.开门();
}
操作(大象){
大象.进入(冰箱);
}
关闭(冰箱){
冰箱.关门();
}
}
冰箱{
开门(){ }
关门(){ }
}
大象{
进入(冰箱){ }
}
|
Java语言的基本元素:类和对象
抽象出来的美人鱼的特征,可以归纳为一个 美人鱼类 。而图 片中的都是这个类呈现出来的 具体的对象 。
类和对象概述
类(Class) 和 对象(Object) 是面向对象的核心概念。
- 类:具有相同特征的事物的抽象描述,是
抽象
的 、概念上的定义。
- 对象:实际存在的该类事物的 每个个体 ,是
具体
的 ,因而也称为 实例
(instance) 。
- 可以理解为: 类 => 抽象概念的人 ; 对象 => 实实在在的某个人
类的成员概述
面向对象程序设计的重点是 类的设计
类的设计,其实就是 类的成员的设计
类,是一组相关 属性
和 行为
的集合,这也是类最基本的两 个成员。
面向对象完成功能的三步骤
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
public class Person {
//属性
String name;//姓名
int age;//年龄
char gender;//性别
//方法
public void eat(){
System.out.println("人吃饭");
}
public void sleep(int hour){
System.out.println("人至少保证明天" + hour + "小时的睡眠");
}
public void interests(String hobby){
System.out.println("我的爱好是:" + hobby);
}
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
|
public class PersonTest {
public static void main(String[] args) {
//创建对象、类的实例化
Person p1 = new Person();
//通过对象调用属性或方法
p1.name = "杰克";
p1.age = 24;
p1.gender = '男';
System.out.println("name = " + p1.name + ",age = " + p1.age +
", gender = " + p1.gender);
p1.eat();
p1.sleep(8);
p1.interests("画画");
//再创建Person类的一个实例
Person p2 = new Person();
p2.name = "露丝";
p2.age = 18;
p2.gender = '女';
System.out.println("name = " + p2.name + ",age = " + p2.age +
", gender = " + p2.gender);
System.out.println("name = " + p1.name + ",age = " + p1.age +
", gender = " + p1.gender);
new Person().sleep(8);
}
}
name = 杰克,age = 24, gender = 男
人吃饭
人至少保证明天8小时的睡眠
我的爱好是:画画
name = 露丝,age = 18, gender = 女
name = 杰克,age = 24, gender = 男
人至少保证明天8小时的睡眠
|
内存解析
堆(Heap) :此内存区域的唯一目的就是存放对象实例
,几乎所有的对象实例都在这里分配内存。这一 点在Java虚拟机规范中的描述是:所有的对象实例以及数组都要在堆上分配。new出来的结构 ,包括对象中的属性
栈(Stack) :是指虚拟机栈。虚拟机栈用于存储局部变量
等。局部变量表存放了编译期可知长度的各 种基本数据类型(boolean、byte、char、short、int、float、long、double)、对象引用(reference类 型,它不等同于对象本身,是对象在堆内存的首地址)。 方法执行完,自动释放。
方法区(Method Area) :用于存储已被虚拟机加载的类信息
、常量、静态变量、即时编译器编译后的代码
等数据。


创建类的多个对象时,每个对象在堆空间中有一个对象实体。每个对象实体中保存着一份类的属性。
如果修改某一个对象的某属性值时,不会影响其他对象此属性的值。
p1,p3 两个变量指向了堆空间中的同一个对象实体。(或p1,p3保存的地址值相同)
如果通过其中某一个对象变量修改对象的属性时,会影响另一个对象变量此属性的值。
对象名中存储的是什么呢? 答:对象地址
直接打印对象名和数组名都是显示“类型@对象的hashCode值",所以说类、数组都是引用数据类型,引 用数据类型的变量中存储的是对象的地址,或者说指向堆中对象的首地址。
1
2
3
4
5
6
7
|
Person p1 = new Person();
System.out.println(p1);
int[] arr =new int[5];
System.out.println(arr);
com.kong.core.Person@4eec7777
[I@3b07d329
|
类的成员之一:属性field
属性的几个称谓:成员变量、属性、field(字段、域)
变量的分类:
- 角度一:按照数据类型来分:基本数据类型(8种)、引用数据类型(数组、类、接口、枚举、注解、记录)
- 角度二:按照变量在类中声明的位置的不同:成员变量(或属性)、局部变量(方法内、方法形参、构造器内、构造器形参、代码块内等)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
|
public class FieldTest {
public static void main(String[] args) {
Person p1 = new Person();
System.out.println(p1.name + "," + p1.age);
p1.sleep(8);
p1.eat();
}
}
class Person{
//属性(或成员变量)
String name;
int age;
char gender;
//方法
public void eat(){
String food = "宫保鸡丁"; //局部变量
System.out.println("我喜欢吃" + food);
}
public void sleep(int hour){ //形参:属于局部变量
System.out.println("人不能少于" + hour + "小时的睡眠");
//编译不通过,因为超出了food变量的作用域
// System.out.println("我喜欢吃" + food);
//编译通过。
System.out.println("name = " + name);
}
}
|
一般需要一个类就新建一个class文件
区分成员变量 vs 局部变量
相同点:
- 变量声明的格式相同:数据类型 变量名 = 变量值
- 变量都有其有效的作用域。出了作用域,就失效了。
- 变量必须先声明,后赋值,再使用。
不同点:
① 类中声明的位置的不同:
属性:声明在类内,方法外的变量
局部变量:声明方法、构造器内部的变量
② 在内存中分配的位置不同:
属性:随着对象的创建,存储在堆空间中
。
局部变量:存储在栈空间中
③ 生命周期:
属性:随着对象
的创建而创建,随着对象的消亡而消亡。
局部变量:随着方法
对应的栈帧入栈,局部变量会在栈中分配;随着方法对应的栈帧出栈,局部变量消亡。
④ 作用域:
属性:在整个类
的内部都是有效的
局部变量:仅限于声明此局部变量所在的方法
(或构造器、代码块)中
⑤ 是否可以有权限修饰符进行修饰:(难)
都有哪些权限修饰符:public、protected、缺省、private。(用于表明所修饰的结构可调用的范围的大小)
属性,是可以使用权限修饰符进行修饰的
。 暂时还未讲封装性,所以大家先不用写任何权限符。
而局部变量,不能使用任何权限修饰符进行修饰的。
⑥ 是否有默认值:(重点)
属性:都有默认初始化值。意味着,如果没有给属性进行显式初始化赋值,则会有默认初始化值。
局部变量:都没有默认初始化值。意味着,在使用局部变量之前,必须要显式的赋值,否则报错。注意:对于方法的形参而言,在调用方法时,给此形参赋值即可。
对象属性的默认初始化赋值
当一个对象被创建时,会对其中各种类型的成员变量自动进行初始化赋值。
角度二:从下而上
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
|
public class MyDate {
int year; //年
int month; //月
int day; //日
}
public class Employee {
int id; //编号
String name; //姓名
int age; //年龄
double salary; //薪资
MyDate birthday; //生日,自己生命的类作为变量
}
public class EmployeeTest {
public static void main(String[] args) {
//创建Employee的一个实例
Employee emp1 = new Employee();
emp1.id = 1001;
emp1.name = "杰克"; //emp1.name = new String("杰克");
emp1.age = 24;
emp1.salary = 8900;
emp1.birthday = new MyDate();//emp1.birthday存放的mydate类的地址
emp1.birthday.year = 1998;
emp1.birthday.month = 2;
emp1.birthday.day = 28;
/*
另一种写法:
* MyDate mydate1 = new MyDate();
* emp1.birthday = mydate1;
* */
//打印员工信息
System.out.println("id = " + emp1.id + ",name = " + emp1.name +
", age = " + emp1.age + ", salary = " + emp1.salary +
", birthday = [" + emp1.birthday.year + "年" + emp1.birthday.month +
"月" + emp1.birthday.day + "日]");
}
}
|
类的成员之二:方法(method)
- 方法是类或对象行为特征的抽象,用来完成某个功能操作。在某些语言中也称为 函数 或 过程 。
- 将功能封装为方法的目的是,可以 实现代码重用,减少冗余,简化代码
- Java里的方法不能独立存在 ,所有的方法必须定义在类里。
声明方法
1
2
3
|
[修饰符] 返回值类型 方法名([形参列表])[throws 异常列表]{
方法体的功能代码
}
|
- 一个完整的方法 = 方法头 + 方法体。
- 方法头就是 [修饰符] 返回值类型 方法名([形参列表])[throws 异常列表] ,也称为 方法签名 。通 常调用方法时只需要关注方法头就可以,从方法头可以看出这个方法的功能和调用格式。
- 方法体就是方法被调用后要执行的代码。对于调用者来说,不了解方法体如何实现的,并不影响方 法的使用。
-
权限修饰符: private \ 缺省 \ protected \ public
-
返回值类型:描述当调用完此方法时,是否需要返回一个结果。
无返回值类型:使用void表示即可。
比如:System.out.println(x)的println(x)方法、Arrays的sort()
有具体的返回值类型:需要指明返回的数据的类型。
可以是基本数据类型,也可以引用数据类型
需要在方法内部配合使用"return + 返回值类型的变量或常量"
如:Math.random()、new Scanner(System.in).nextInt()等
-
方法名:属于标识符。需要满足标识符的规定和规范。“见名知意”
-
形参列表:形参,属于局部变量,且可以声明多个。
格式:(形参类型1 形参1,形参类型2 形参2,…)
无形参列表:不能省略一对()。比如:Math.random()、new Scanner(System.in).nextInt()
有形参列表:根据方法调用时,需要的不确定的变量的类型和个数,确定形参的类型和个数。
比如:Arrays类中的binarySearch()方法、sort()方法、equals()方法
-
方法体: 当我们调用一个方法时,真正执行的代码。体现了此方法的功能
Java里的方法不能独立存在
,所有的方法必须定义在类里。
Java中的方法不调用,不执行。每调用一次,就执行一次。
方法内可以调用本类中的(其它)方法
或属性
方法内不能定义方法。
方法区(Method Area):用于存储已被虚拟机加载的类信息
、常量、静态变量、即时编译器编译后的 代码等数据。
Person p1 = new Person();
创建对象p1之前,person类的信息已经先加载到方法区
中了,所有方法内可以调用其他本类中的方法。
关键字:return
return的作用
- 作用1:结束一个方法
- 作用2:结束一个方法的同时,可以返回数据给方法的调用者 (方法声明中如果有返回值类型,则方法内需要搭配return使用)
使用注意点:
return后面不能声明执行语句。
方法调用的内存解析
- 形参:方法在声明时,一对()内声明的一个或多个形式参数,简称为形参。
- 实参:方法在被调用时,实际传递给形参的变量或常量,就称为实际参数,简称实参。
每一个方法对应一个栈帧,栈帧是栈中的基本结构,调用一个main方法,就是一个栈帧入栈。
方法执行完毕之后就出栈。
引用类型的变量存放的是地址,string数据放在堆中的字符串常量池中。
GC:垃圾回收器->回收堆中的空间。GC会自动识别和回收程序中不再需要的对象所占用的内存空间,使得开发人员无需手动管理内存,减轻了内存管理的负担。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
public class Circle {
//属性
double radius;//半径
//方法
public void findArea(){
System.out.println("面积为:" + 3.14 * radius * radius);
}
//或
public double findArea2(){
return 3.14 * radius * radius;
}
//错误的:
// public void findArea1(double r){
// System.out.println("面积为:" + 3.14 * r * r);
// }
}
|
求圆的面积,定义r为圆类的属性,而不是传入的参数。更适合面向对象的思想
ctr+f12:显示当前类的成员变量和成员方法

对象数组
数组的元素可以是基本数据类型,也可以是引用数据类型。当元素是引用类型中的类
时,我们称为对象数组。
String[],Person[],Student[],Customer[]
对象数组,首先要创建数组对象本身,即确定数组的长度,然后再创建每一个元素对象,如果不创建, 数组的元素的默认值就是 null ,所以很容易出现 空指针异常NullPointerException 。
1
2
3
4
5
6
7
8
9
10
11
12
13
|
public class Student {
//属性
int number;//学号
int state;//年级
int score;//成绩
//声明一个方法,显示学生的属性信息
public String show(){
return "number : " + number + ",state : " +
state + ", score : " + score;
}
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
|
public class StudentTest {
public static void main(String[] args) {
//创建Student[]
Student[] students = new Student[20]; //String[] strs = new String[20];
//使用循环,给数组的元素赋值
for (int i = 0; i < students.length; i++) {
students[i] = new Student();
//给每一个学生对象的number、state、score属性赋值
students[i].number = i + 1;
students[i].state = (int)(Math.random() * 6 + 1);
students[i].score = (int)(Math.random() * 101);
}
//需求1:打印出3年级(state值为3)的学生信息
for (int i = 0; i < students.length; i++) {
if(3 == students[i].state){
Student stu = students[i];
System.out.println(stu.show());
}
}
//需求2:使用冒泡排序按学生成绩排序,并遍历所有学生信息
//排序前遍历
for (int i = 0; i < students.length; i++) {
System.out.println(students[i].show());
}
System.out.println("********************");
for (int i = 0; i < students.length - 1; i++) {
for (int j = 0; j < students.length - 1 - i; j++) {
if(students[j].score > students[j + 1].score){
//错误的,不满足实际需求!
// int temp = students[j].score;
// students[j].score = students[j + 1].score;
// students[j + 1].score = temp;
//正确的
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].show());
}
}
}
|
提供封装Student相关操作的工具类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
|
public class StudentUtil {
/*
* 打印出指定年级的学生信息
* */
public void printStudentsWithState(Student[] students,int state){
for (int i = 0; i < students.length; i++) {
if(state == students[i].state){
Student stu = students[i];
System.out.println(stu.show());
}
}
}
/*
* 遍历指定的学生数组
* */
public void printStudents(Student[] students){
for (int i = 0; i < students.length; i++) {
System.out.println(students[i].show());
}
}
/*
* 针对于学生数组,按照score属性从低到高排列
* */
public void sortStudents(Student[] students){
for (int i = 0; i < students.length - 1; i++) {
for (int j = 0; j < students.length - 1 - i; j++) {
if(students[j].score > students[j + 1].score){
Student temp = students[j];
students[j] = students[j + 1];
students[j + 1] = temp;
}
}
}
}
}
|
内存解析

方法重载
-
方法重载:在同一个类中,允许存在一个以上的同名方法,只要它们的参数列表
不同即可。
参数列表不同,意味着参数个数
或参数类型
的不同
-
重载的特点:与修饰符、返回值类型无关,只看参数列表,且参数列表必须不同。(参数个数或参数类型)。调用时,根据方法参数列表的不同来区别。
-
重载方法调用:JVM通过方法的参数列表,调用匹配的方法。
先找个数、类型最匹配
的
再找个数和类型可以兼容
的,如果同时多个方法可以兼容将会报错
注意:方法的重载与形参的名、权限修饰符、返回值类型都没有关系。
如何判断两个方法是相同的呢?(换句话说,编译器是如何确定调用的某个具体的方法呢?)
方法名相同,且形参列表相同。(形参列表相同指的是参数个数和类型都相同,与形参名没关系)
要求:在一个类中,允许存在多个相同名字的方法,只要他们的形参列表
不同即可。
编译器是如何确定调用的某个具体的方法呢?先通过方法名确定了一波重载的方法,进而通过不同的形参列表,确定具体的某一个方法。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
public int max(int i,int j){
return (i >= j)? i : j;
}
public double max(double d1,double d2){
return (d1 >= d2)? d1 : d2;
}
public double max(double d1,double d2,double d3){
// double tempMax = max(d1,d2);
// return max(tempMax,d3);
return (max(d1,d2) > d3)? max(d1,d2) : d3;
}
|
1
2
3
4
5
6
7
8
|
int[] arr = new int[]{1, 2, 3};
System.out.println(arr);//地址值[I@776ec8df
char[] arr1 = new char[]{'a', 'b', 'c', 'd', 'e'};
System.out.println(arr1);//abcde
boolean[] arr2 = new boolean[]{false, true, true};
System.out.println(arr2);//地址值[Z@4eec7777
|
println的重载
数组是引用类型,调用的是println(Object x),默认打印的是地址。
唯独当数组是char类型数组的时候,打印的是数组的遍历

可变个数的形参
在JDK 5.0 中提供了Varargs(variable number of arguments)机制。即当定义一个方法时,形参的类型可以 确定,但是形参的个数不确定,那么可以考虑使用可变个数的形参。
1
2
3
4
5
|
格式:(参数类型 ... 参数名)
public void print(int ... nums){
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
|
public class ArgsTest {
public static void main(String[] args) {
ArgsTest test = new ArgsTest();
test.print();
test.print(1);
test.print(1,2);
test.print(new int[]{1,2,3});
//会报错分不出是int ... nums还是,int i,int ... nums
//两个方法实际上是一样的,后者的int i 改为double i就不错
// test.print(1,2,3);
}
public void print(int ... nums){
System.out.println("1111");
//遍历
for (int i = 0; i < nums.length; i++) {
System.out.println(nums[i]);
}
}
//不构成重载,有冲突,等价于上述方法
// public void print(int[] nums){
// for (int i = 0; i < nums.length; i++) {
// System.out.println(nums[i]);
// }
// }
//构成重载
public void print(int i){
System.out.println("2222");
}
//构成重载,不报错,但是调用时会引起多个方法同时匹配
public void print(int i,int j){
System.out.println("3333");
}
public void print(int i,int ... nums){
}
//编译不通过
// public void print(int ... nums,int i){
//
// }
}
|
说明:
① 可变个数形参的方法在调用时,针对于可变的形参赋的实参的个数可以为:0个、1个或多个
② 可变个数形参的方法与同一个类中,同名的多个方法之间可以构成重载
–>先找最适配的
③ 特例:可变个数形参的方法与同一个类中方法名相同,且与可变个数形参的类型相同的数组参数
不构成重载。–>5.0以前,不确定参数就是数组表示,所以会冲突–>数组可调用可变形参方法
④ 可变个数的形参必须声明在形参列表的最后
⑤ 可变个数的形参最多在一个方法的形参列表中出现一次
1
2
3
4
5
6
7
8
|
//场景举例:
String sql = "update customers set name = ?,email = ? where id = ?";
String sql1 = "update customers set name = ? where id = ?";
//不确定sql语句中的参数有几个,sql有几个问号,objs就有几个参数
public void update(String sql,Object ... objs);
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
|
public class StringConCatTest {
public static void main(String[] args) {
StringConCatTest test = new StringConCatTest();
String info = test.concat("-","hello","world");
System.out.println(info);//hello-world
System.out.println(test.concat("/", "hello"));
System.out.println(test.concat("-"));
}
//n个字符串进行拼接,每一个字符串之间使用某字符进行分割,如果没有传入字符串,那么返回空字符串""
public String concat(String operator,String ... strs){
String result = "";
for (int i = 0;i < strs.length;i++){
if(i == 0){
result += strs[i];
}else{
result += (operator + strs[i]);
}
}
return result;
}
}
|
方法的参数传递机制
对于方法内声明的局部变量来说:如果出现赋值操作
如果是基本数据类型的变量,则将此变量保存的数据值传递出去。
如果是引用数据类型的变量,则将此变量保存的地址值传递出去。
方法的参数的传递机制:值传递机制
形参:在定义方法时,方法名后面括号()中声明的变量称为形式参数,简称形参。
实参:在调用方法时,方法名后面括号()中的使用的值/变量/表达式称为实际参数,简称实参。
规则:实参给形参赋值的过程
如果形参是基本数据类型的变量,则将实参保存的数据值赋给形参
。
如果形参是引用数据类型的变量,则将实参保存的地址值赋给形参
。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
|
public class ValueTransferTest1 {
public static void main(String[] args) {
ValueTransferTest1 test = new ValueTransferTest1();
//1. 对于基本数据类型的变量来说,传递值
int m = 10;
test.method1(m);//将m的值赋给了method1方法的形参
System.out.println("m = " + m);//10
//2. 对于引用数据类型的变量来说,传递地址
Person p = new Person();
p.age = 10;
test.method2(p);//将p的地址值传给了method2方法的形参
System.out.println(p.age); //11
}
public void method1(int m){
m++;//仅修改了方法内的参数的值,main方法中的m不变
}
public void method2(Person p){
p.age++;//传递的地址,可以修改main中的p
}
}
class Person{
int age;
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
|
public class ValueTransferTest2 {
public static void main(String[] args) {
ValueTransferTest2 test = new ValueTransferTest2();
int m = 10;
int n = 20;
System.out.println("m = " + m + ", n = " + n);
//交换两个变量的值
//操作1:
// int temp = m;
// m = n;
// n = temp;
//操作2:
//调用方法---并没有交换
test.swap(m,n);
System.out.println("m = " + m + ", n = " + n);//10,20
}
public void swap(int m ,int n){
int temp = m;
m = n;
n = temp;
}
}
|

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
|
public class ValueTransferTest3 {
public static void main(String[] args) {
ValueTransferTest3 test = new ValueTransferTest3();
Data data = new Data();
data.m = 10;
data.n = 20;
System.out.println("m = " + data.m + ", n = " + data.n);
//操作1:
// int temp = data.m ;
// data.m = data.n;
// data.n = temp;
//操作2:
test.swap(data);
System.out.println("m = " + data.m + ", n = " + data.n);
}
public void swap(Data data){
int temp = data.m ;
data.m = data.n;
data.n = temp;
}
}
class Data{
int m;
int n;
}
|

排序
asc:升序 desc:降序
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
|
/**
* 针对于数组进行排序操作
* @param arr 待排序的数组
* @param sortMethod asc:升序 desc:降序
*/
public void sort(int[] arr,String sortMethod){
////if(sortMethod.equals("asc")){
// 当sortmethod为null是会空指针异常acs放前面就可以避免
if("asc".equals(sortMethod)){//ascend:升序
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]){
//交互arr[i] 和 arr[i + 1]
// int temp = arr[i];
// arr[i] = arr[i + 1];
// arr[i + 1] = temp;
//错误的:
// swap(arr[i],arr[i + 1]);
//正确的:
swap(arr,i,i+1);
}
}
}
}else if("desc".equals(sortMethod)){
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]){
//交互arr[i] 和 arr[i + 1]
// int temp = arr[i];
// arr[i] = arr[i + 1];
// arr[i + 1] = temp;
//错误的:
// swap(arr[i],arr[i + 1]);
//正确的:
swap(arr,i,i+1);
}
}
}
}else{
System.out.println("你输入的排序方式有误");
}
}
// public void swap(int i,int j){//实参接收的数值
// int temp = i;
// i = j;
// j = temp;
// }
private void swap(int[] arr,int i,int j){//接收的地址
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
|
asc,desc字面量写前面,判断相等,规避空指针异常
“asc”.equals(sortMethod)
交换数组两个位置,硬传递数组–(对应地址)和位置
递归方法
递归方法调用:方法自己调用自己的现象就称为递归。
递归的分类:直接递归、间接递归。
- 直接递归:方法自身调用自己。
- 间接递归:可以理解为A()方法调用B()方法,B()方法调用C()方法,C()方法调用A()方法。
说明:
- 递归方法包含了一种
隐式的循环
。
- 递归方法会
重复执行
某段代码,但这种重复执行无须循环控制。
- 递归一定要向
已知方向
递归,否则这种递归就变成了无穷递归,停不下来,类似于死循环
。最终发生栈内存溢出
。
注意:
- 递归调用会占用大量的系统堆栈,内存耗用多,在递归调用层次多时速度要比循环
慢的多
,
所以在使用递归时要慎重。
- 在要求高性能的情况下尽量避免使用递归,递归调用既花时间又
耗内存
。考虑使用循环迭代
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
/*
* 举例1:计算1-100内自然数的总和
* */
public int getSum(int num){
int sum = 0;
for(int i = 1;i <= num;i++){
sum += i;
}
return sum;
}
public int getSum1(int num){
if(num == 1){//已知方向
return 1;
}else{
return getSum1(num - 1) + num;
}
}
|
汉诺塔游戏
斐波那契数列
1 1 2 3 5 8 13 21 34 55 …
f(n) = f(n - 1) + f(n - 2)
1
2
3
4
5
6
7
8
9
|
public int f(int n){
if(n == 1){
return 1;
}else if(n == 2){
return 1;
}else{
return f(n - 1) + f(n - 2);
}
}
|
关键字:package、import
- 说明
- package:包
- package用于指明该文件中定义的类、接口等结构所在的包
- 一个源文件只能有一个声明包的package语句
- package语句作为Java源文件的第一条语句出现。若缺省该语句,则指定为无名包。
- 包名,属于标识符,满足标识符命名的规则和规范(全部小写)、见名知意
- 包通常使用所在公司域名的倒置:com.atguigu.xxx。
- 大家取包名时不要使用"
java.xx
“包
- 包对应于文件系统的目录,package语句中用 “.” 来指明包(目录)的层次,每.一次就表示一层文件目录。
- 同一个包下可以声明多个结构(类、接口),但是不能定义同名的结构(类、接口)。不同的包下可以定义同名的结构(类、接口)
- 包的作用
- 包可以包含类和子包,划分
项目层次
,便于管理
- 帮助
管理大型软件
系统:将功能相近的类划分到同一个包中。比如:MVC的设计模式
- 解决
类命名冲突
的问题
- 控制
访问权限
- JDK中主要的包
java.lang
—-包含一些Java语言的核心类,如String、Math、Integer、 System和Thread,提供常用功能
java.net
—-包含执行与网络相关的操作的类和接口。
java.io
—-包含能提供多种输入/输出功能的类。
java.util
—-包含一些实用工具类,如定义系统特性、接口的集合框架类、使用与日期日历相关的函数。
java.text
—-包含了一些java格式化相关的类
java.sql
—-包含了java进行JDBC数据库编程的相关类/接口
java.awt
—-包含了构成抽象窗口工具集(abstract window toolkits)的多个类,这些类被用来构建和管理应用程序的图形用户界面(GUI)。
MVC设计模式
MVC是一种软件构件模式,目的是为了降低程序开发中代码业务的耦合度。
MVC设计模式将整个程序分为三个层次: 视图模型(Viewer)层 , 控制器(Controller)层 ,与 数据模型 (Model)层 。这种将程序输入输出、数据处理,以及数据的展示分离开来的设计模式使程序结构变的灵活而且清晰,同时也描述了程序各个对象间的通信方式,降低了程序的耦合性。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
视图层viewer:显示数据,为用户提供使用界面,与用户直接进行交互。
>相关工具类 view.utils
>自定义view view.ui
控制层controller:解析用户请求,处理业务逻辑,给予用户响应
>应用界面相关 controller.activity
>存放fragment controller.fragment
>显示列表的适配器 controller.adapter
>服务相关的 controller.service
>抽取的基类 controller.base
模型层model:主要承载数据、处理数据
>数据对象封装 model.bean/domain
>数据库操作类 model.dao
>数据库 model.db
|
import关键字的使用
-
import : 导入
-
import语句来显式引入指定包下所需要的类。相当于import语句告诉编译器到哪里去寻找这个类
。
-
import语句,声明在包的声明和类的声明之间。
-
如果需要导入多个类或接口,那么就并列显式多个import语句即可
-
如果使用a.*
导入结构,表示可以导入a包下的所有的结构。举例:可以使用java.util.*的方式,一次性导入util包下所有的类或接口。
-
如果导入的类或接口是java.lang包下的,或者是当前包下的,则可以省略此import语句。
-
如果已经导入java.a包下的类,那么如果需要使用a包的子包下的类的话,仍然需要导入
。
-
如果在代码中使用不同包下的同名的类,那么就需要使用类的全类名的方式指明调用的是哪个类。
-
(了解)import static
组合的使用:调用指定类或接口下的静态的属性或方法
1
2
3
4
|
import static java.lang.System.out;
import static java.lang.Math.PI;
out.println(PI);
|
封装性
面向对象特征之一:封装性(encapsulation)
面向对象的开发原则要 遵循 “高内聚、低耦合”。
高内聚、低耦合是软件工程中的概念,也是UNIX 操作系统设计的经典原则。
内聚,指一个模块内各个元素彼此结合的紧密程度;
耦合指一个软件结构内不同模块之间互连程度 的度量。
内聚意味着重用和独立,耦合意味着多米诺效应牵一发动全身。
-高内聚
:类的内部数据操作细节自己完成,不允许外部干涉;
-低耦合
:仅暴露少量的方法给外部使用,尽量方便外部调用。
所谓封装,就是把客观事物封装成抽象概念的类,并且类可以把自己的数据和方法只向可信的类或者对 象开放,向没必要开放的类或者对象隐藏信息。
通俗的说:把该隐藏的隐藏起来,该暴露的暴露出来。这就是封装性的设计思想。
权限修饰符
实现封装就是控制类或成员的可见性范围。这就需要依赖访问控制修饰符,也称为权限修饰符来控制。
权限修饰符: public 、 protected 、 缺省 、 private 。我们可以使用4种权限修饰来修饰类及类的内部成员。当这些成员被调用时,体现可见性的大小
类:只能使用public、缺省修饰
类的内部成员:可以使用4种权限修饰进行修饰。
操作修饰符 |
本类内部 |
本包内 |
其他包的子类 |
其他包非子类 |
private |
√ |
|
|
|
缺省 |
√ |
√ |
|
|
protected |
√ |
√ |
√ |
|
public |
√ |
√ |
√ |
√ |
封装性的体现:
场景1:私有化(private)类的属性,提供公共(public)的get和set方法,对此属性进行获取或修改
场景2:将类中不需要对外暴露的方法,设置为private.
场景3:单例模式中构造器private的了,避免在类的外部创建实例。
成员变量/属性私有化
概述:私有化类的成员变量,提供公共的get和set方法,对外暴露获取和修改属性的功能。
- ① 使用 private 修饰成员变量
- ② 提供 getXxx 方法 / setXxx 方法,可以访问成员变量
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
class Animal{ //动物
//属性
String name; //名字
private int legs;//腿的个数
//方法
//设置legs的属性值
public void setLegs(int l){
if(l >= 0 && l % 2 == 0){
legs = l;
}else{
System.out.println("你输入的数据非法");
}
}
//获取legs的属性值
public int getLegs(){
return legs;
}
}
|
我们知道legs不能赋值为负数的。但是如果直接调用属性legs,是不能加入判断逻辑的。那怎么办呢?
将legs属性私有化(private),禁止在Animal类的外部直接调用此属性
提供给legs属性赋值的setLegs()方法,在此方法中加入legs赋值的判断逻辑if(legs >= 0 && legs % 2 ==0)将此方法暴露出去,使得在Animal类的外部调用此方法,对legs属性赋值。
提供给legs属性获取的getLegs()方法,此方法对外暴露。使得在Animal类的外部还可以调用此属性的值。
成员变量封装的好处:
让使用者只能通过事先预定的方法来 访问数据 ,从而可以在该方法里面加入控制逻辑,限制对成员 变量的不合理访问。还可以进行数据检查,从而有利于保证对象信息的完整性。
便于修改 ,提高代码的可维护性。主要说的是隐藏的部分,在内部修改了,如果其对外可以的访问 方式不变的话,外部根本感觉不到它的修改。例如:Java8->Java9,String从char[]转为byte[]内部实 现,而对外的方法不变,我们使用者根本感觉不到它内部的修改。
类的成员之三:构造器(Constructor)
构造器的作用
: new对象,并在new对象的时候为实例变量赋值。
说明:
构造器名必须与它所在的类名必须相同。
它没有返回值,所以不需要返回值类型,也不需要void。
构造器的修饰符只能是权限修饰符,不能被其他任何修饰。比如,不能被static、final、 synchronized、abstract、native修饰,不能有return语句返回值。
创建类以后,在没有显示提供任何构造器的情况下,系统会默认提供一个空参的构造器,且构造器的权限
与类声明的权限相同。
一旦类中显示声明了构造器,则系统不再提供默认的空参的构造器。
一个类中可以声明多个构造器,彼此之间构成重载。
1
2
3
4
5
6
7
8
9
10
11
12
13
|
//默认的空参构造器
public Person(){
System.out.println("Person()....");
}
//声明其它的构造器
public Person(int a){
age = a;
}
//构造器的使用
Person p1 = new Person();
Person p2 = new Person(1);
|
匿名对象 (anonymous object)
- 我们也可以不定义对象的句柄,而直接调用这个对象的方法。这样的对象叫做匿名对象。
- 如:new Person().sleep(8);
- 使用情况 如果一个对象只需要进行一次方法调用,那么就可以使用匿名对象。
- 我们经常将匿名对象作为实参传递给一个方法调用。
1
|
customer.setAccount(new Account(1000,2000,0.0123));
|
类中属性赋值过程
实例变量
- 在类的属性中,可以有哪些位置给属性赋值?
① 默认初始化;
② 显式初始化;
③ 构造器中初始化;
④ 通过"对象.方法"的方式赋值;
⑤ 通过"对象.属性"的方式赋值;
-
这些位置执行的先后顺序是怎样?
① - ② - ③ - ④/⑤
-
以上操作在对象创建过程中可以执行的次数如何?
只能执行一次:①、②、③
可以多次执行:④、⑤
JavaBean
所谓JavaBean,是指符合如下标准的Java类:
- 类是公共的
- 有一个无参的公共的构造器
- 有属性,且有对应的get、set方法
JavaBean是一种符合特定约定的Java类,通常用于封装数据和提供访问这些数据的方法。其主要作用是作为一种轻量级的可重用组件,用于在Java应用程序中管理数据。
UML类图
在软件开发中,使用 UML类图 可以更加直观地描述类内部结构(类的属性和操作)以及类之间的关系(如关联、依赖、聚合等)。
- +表示 public 类型, - 表示 private 类型,#表示protected类型
- 方法的写法: 方法的类型(+、-) 方法名(参数名: 参数类型):返回值类型
- 斜体表示抽象方法或类。
