1 面向对象思想概述
1.1 概述
-
Java语言是一种面向对象的程序设计语言,面向对象思想(OOP)是一种程序设计思想,我们在面向对象思想的指引下,使用Java语言去设计、开发计算机程序。
-
这里的
对象
泛指现实中的一切事物,每种事务都具备自己的属性
和行为
。 -
面向对象思想就是在计算机程度设计的过程中,参照现实中的事物,将事物的属性特征、行为特征抽象出来,描述成计算机事物的设计思想。
-
面向对象思想区别于面向过程思想(POP),强调的是通过调用对象的行为来实现功能,而不是自己一步一步的去操作实现。
1.2 面向过程和面向对象的区别
-
面向过程(POP):
Process Oriented Programming
。 -
- 以函数(方法)为最小单位。
-
- 数据独立于函数之外。
-
- 以过程、步骤为主,考虑怎么做?
-
面向对象(OOP):
Object Oriented Programming
。 -
- 以类、对象为最小单位。
-
- 类包括数据和方法。
-
- 以对象(谁)为主,考虑谁来做,谁能做?
-
面向对象依然包含面向过程,只不过关注点变了,关注谁来做。
-
面向过程中程序员的角色:程序员是具体执行者。
-
面向对象中程序员的角色:程序员是指挥者。
-
面向对象思想是一种更符合我们思考习惯的思想,它可以将复杂的事情简单化,并将我们从执行者变成了指挥者。
-
例子:将大象装进冰箱
-
面向过程:
① 打开冰箱
② 将大象装进冰箱
③ 将冰箱门关闭
- 面向对象:
冰箱{
开门(){}
关门(){}
}
大象{
进入(冰箱){}
}
人{
打开(冰箱){}
操作(大象){
大象.进入(冰箱);
}
关闭(冰箱){
冰箱.关门();
}
}
1.3 面向对象的基本特征
- 面向对象的语言中,包含了三大基本特征:
封装
、继承
、多态
。
2 类和对象
2.1 概述
- 环顾周围,我们会发现很多对象,比如:桌子、椅子、电脑、同事、同学等。桌子属于办公用品,同事和同学都是人类,那么什么是类?什么是对象?
2.2 什么是类?
-
类是一类具有相同特性的事物的抽象描述,是一组相关
属性
和行为
的集合。可以将类看成是一类事物的模板,使用事物的属性特性
和行为特性
来描述该类事物。 -
类可以看做是一个模板,或者图纸,系统根据类的定义来创建对象,以后我们要造一个汽车,怎么造?类就是这个图纸,规定了汽车的详细信息,然后根据图纸将汽车造出来。
-
示例:英雄联盟中的类和对象
英雄就是类,具体的英雄,如盖伦、提莫就是对象。
- 示例:月饼模具和月饼
月饼模具是类,使用月饼模具制作的月饼就是对象。
-
现实生活中,描述一类事物:
-
属性
:就是该事物的状态信息。
-
行为
:就是该事物能够做什么?
-
示例:猫
-
属性:名字、体重、年龄、颜色。
-
行为:走、跑、叫。
2.3 什么是对象?
-
对象:就是一类事物的具体体现。对象是类的一个
实例
,必然具有该类事物的属性和行为。 -
示例:一只小猫
-
属性:tom、5kg、2岁,黄色。
-
行为:溜墙根走、蹦跶的跑、喵喵叫。
2.4 类和对象的关系
-
类是对一类事物的描述,是
抽象的
。 -
对象是一类事物的实例,是
具体的
。 -
类是对象的模板,对象是类的实体
。
3 类的定义
3.1 事物和类的对比
-
现实世界中的一类事物:
-
属性
:事物的状态信息。
-
行为
:事物能够做什么。
-
Java中用
class
描述事物也是如此: -
成员变量
:对应事物的属性
。
-
成员方法
:对应事物的行为
。
3.2 类的定义
- 语法:
访问修饰符 class 类名{
// 成员变量
// 成员方法
}
-
定义类
:就是定义类的成员,包括成员变量
和成员方法
。 -
成员变量
:和以前定义变量几乎一模一样,只不过位置发生了改变。在类中,方法外
。 -
成员方法
:和以前写的main方法类似,只不过功能更加丰富了。 -
示例:
/**
* 学生类
*
* @author open1024
* @version 1.0
* @since 2021-08-26 19:23
*/
public class Student {
/**
* 姓名
*/
String name;
/**
* 年龄
*/
int age;
/**
* 性别
*/
char gender;
/**
* 学习的方法
*/
public void study() {
System.out.println("学习使我快乐");
}
}
4 对象的创建和使用
4.1 对象的创建和使用
- 对象的创建语法:
// 匿名对象
new 类名();
// 给创建的对象命名,或者说,将创建的对象用一个引用数据类型的变量保存起来
类名 对象名 = new 类名();
- 对象的使用语法:
// 赋值
对象名.成员变量 = 值;
// 访问
对象名.成员变量;
- 示例:
/**
* @author open1024
* @version 1.0
* @since 2021-08-26 19:30
*/
public class StudentTest {
public static void main(String[] args) {
// 匿名对象
System.out.println(new Student());
// 把创建的对象用一个引用数据类型的变量保存起来
Student student = new Student();
System.out.println("student = " + student);
student.name = "法外狂徒--张三";
student.age = 18;
student.gender = '男';
System.out.println("姓名:" + student.name + ",年龄:" + student.age + ",性别:" + student.gender);
student.study();
}
}
4.2 类的访问机制
-
在一个类中的访问机制
:类中的方法可以直接访问类中的成员变量,但是static方法访问非static的成员变量,编译不通过。 -
在不同类中的访问机制
:先创建要访问类的对象,然后用对象访问类中定义的成员。
4.3 匿名对象
-
可以不定义对象的句柄,而直接调用这个对象的方法,这样的对象就叫做匿名对象。
-
匿名对象的语法:
new 类名();
-
应用场景:
-
- 如果对一个对象只需要进行一次方法调用,那么就可以使用匿名对象。
-
- 实际开发中,经常将匿名对象作为实参传递给一个方法调用。
5 关键字 package和import
5.1 package(包)
-
package语句作为Java源文件的第一条语句,指明该文件中定义的类所在的包(如果缺省该语句,则指定为无名包)。
-
语法:
package 顶层包名.子包名;
-
包对应于文件系统的目录,package语句中,用
.
来指明包(目录)的层次。 -
包通常用小写单词标识。通常使用所在公司域名的倒置,比如:
com.github.xxx
。 -
包的作用:
-
- 包帮助管理大型软件系统:将功能相近的类划分到同一个包中,比如:
MVC设计模式
。
- 包帮助管理大型软件系统:将功能相近的类划分到同一个包中,比如:
-
- 包可以包含类和子包,划分项目层次,便于管理。
-
- 解决类命名冲突的问题。
-
- 控制访问权限。
-
MVC设计模式:
-
- MVC是常用的设计模式之一,将整个程序分为三个层次:
视图模型层
、控制器层
和数据模型层
。这种将程序输入输出、数据处理以及数据的展示分离开来的设计模式,使得程序结构变得灵活而且清晰,同时也描述了程序各个对象间的通信方式,降低了程序的耦合性。
- MVC是常用的设计模式之一,将整个程序分为三个层次:
-
- 模型层(model):主要处理数据。
-
- 控制层(controller):处理业务逻辑。
-
- 视图层(view):显示数据。
-
jdk中的主要包:
-
java.lang
:包含一些Java语言的核心类,如String、Math、Integer、System和Thread,提供常用功能。
-
java.net
:包含执行和网络相关的操作的类和接口。
-
java.io
:包含能提供多种输入、输出的功能的类。
-
java.util
:包含一些实用工具类,如定义系统特性、接口的集合框架类、使用和日期日历相关的函数。
-
java.text
:包含了一些java格式化相关的类。
-
java.sql
:包含了java进行JDBC数据库编程的相关类/接口。
-
java.awt
:包含了构成抽象窗口工具集的多个类,这些类被用来构建和管理应用程序的图形用户界面。
-
示例:
package com.github.model;
/**
* @author open1024
* @version 1.0
* @since 2021-08-27 06:36
*/
public class Student {
String name;
/**
* 年龄
*/
int age;
/**
* 性别
*/
char gender;
/**
* 学习的方法
*/
public void study() {
System.out.println("学习使我快乐");
}
}
5.2 import(导入)
-
为了使用定义在不同包中的Java类,需要使用
import
语句来引入指定包层次下所需要的类或全部类(*
)。 -
import语句告诉编译器到哪里去寻找类
。 -
语法:
import 包名.类名;
-
注意事项:
-
- 在源文件中使用import显示的导入指定包下的类或接口。
-
- import声明在package声明和class声明之间。
-
- 如果需要导入多个类或接口,那么就并列的显示多个import语句即可。
import java.util.*;
-
- 如果导入的类或接口是
java.lang
包下的,或者是当前包下的,则可以省略此import语句。
- 如果导入的类或接口是
-
- 如果在代码中使用不同包下的同名的类,那么就需要使用类的全类名(包名.类名)的方式指定调用的是哪个类。
-
- 如果已经导入
java.a
包下的类,那么如果需要使用a
包的子包下的类,仍然需要导入。
- 如果已经导入
-
- import static组合的使用:调用指定类或接口下的静态的属性或方法。
-
示例:
package com.github.model;
/**
* @author open1024
* @version 1.0
* @since 2021-08-27 06:36
*/
public class Student {
String name;
/**
* 年龄
*/
int age;
/**
* 性别
*/
char gender;
/**
* 学习的方法
*/
public void study() {
System.out.println("学习使我快乐");
}
}
package com.github.service;
import com.github.model.Student;
/**
* @author open1024
* @version 1.0
* @since 2021-08-27 06:42
*/
public class StudentService {
private Student student = new Student();
public void study(){
student.study();
}
}
6 类的成员之一:属性
6.1 语法格式
- 语法:
访问修饰符 class 类名{
访问修饰符 数据类型 属性名 = 初始化值;
访问修饰符 数据类型 属性名;
}
-
访问修饰符:
-
- 常用的权限访问修饰符有:private、缺省、protected、public。
-
- 其他的访问修饰符:static、final。
-
数据类型:任何基本数据类型或任何引用数据类型。
-
属性名:属于标识符,符合命名规则和规范即可。
-
示例:
package com.github.model;
/**
* @author open1024
* @version 1.0
* @since 2021-09-01 06:16
*/
public class Person {
/**
* 声明public类型的变量name
*/
public String name = "张三";
/**
* 声明private类型的变量age
*/
private int age;
}
6.2 变量的分类
-
成员变量:方法体外,类体内声明的变量。
-
局部变量:在方法体内声明的变量。
注意:成员变量和局部变量在初始化值方面的异同。
-
同:都有生命周期。
-
异:局部变量除形参外,均需显示初始化。
-
成员变量和局部变量的区别:
成员变量 | 局部变量 | |
---|---|---|
声明的位置 | 直接声明在类中,方法体外 | 方法形参或内部,代码块内,构造器内等 |
访问修饰符 | private、public、static、final等 | 不能用权限访问修饰符,可以用final修饰 |
初始化值 | 有默认的初始化值 | 没有默认初始化值,必须显示赋值,方可使用 |
内存加载位置 | 堆空间或静态域内 | 栈空间 |
6.3 成员变量的特点
6.3.1 成员变量的默认初始化值
- 当一个对象被创建后,会对其中各种类型的
成员变量
自动进行初始化赋值。除了基本数据类型之外的变量类型都属于引用数据类型。
成员变量类型 | 初始化值 |
---|---|
byte | 0 |
short | 0 |
int | 0 |
long | 0L |
float | 0.0F |
double | 0.0 |
char | ‘\u0000’ |
boolean | false |
引用数据类型(数组,类,接口) | null |
6.3.2 类变量的值是所有对象共享的,而实例变量的值是每个对象独立的
- 示例:
package com.github.demo;
/**
* @author open1024
* @version 1.0
* @since 2021-09-01 06:48
*/
public class ChineseTest {
public static void main(String[] args) {
Chinese c1 = new Chinese();
Chinese c2 = new Chinese();
c1.name = "张三";
c2.name = "李四";
c2.gender = '女';
// c1.country = "中国";
Chinese.country = "中国";// 推荐
System.out.println("c1.country = " + Chinese.country + ",c1.name = " + c1.name + ",c1.gender = " + c1.gender);
System.out.println("c2.country = " + Chinese.country + ",c2.name = " + c2.name + ",c2.gender = " + c2.gender);
}
}
class Chinese {
/**
* 国家
*/
static String country = "中国";
/**
* 姓名
*/
String name;
/**
* 性别
*/
char gender = '男';
}
6.4 如何在类外面访问成员变量?
- 类变量:
类名.静态成员变量 // 推荐
对象名.静态成员变量 // 不推荐
- 实例变量:
对象名.静态成员变量
- 示例:
package com.github.demo;
/**
* @author open1024
* @version 1.0
* @since 2021-09-01 06:48
*/
public class ChineseTest {
public static void main(String[] args) {
// 类名.静态成员变量
System.out.println(Chinese.country);
Chinese c1 = new Chinese();
// 对象名.非静态成员变量
System.out.println(c1.name);
// 对象名.静态成员变量 不推荐
System.out.println(Chinese.country);
// 对象名.非静态成员变量
System.out.println(c1.gender);
}
}
class Chinese {
/**
* 国家
*/
static String country = "中国";
/**
* 姓名
*/
String name;
/**
* 性别
*/
char gender = '男';
}
7 类的成员之二:方法
7.1 什么是方法(函数)?
-
方法是类或对象行为特征的抽象,用来完成某个功能操作。在某些语言中也称之为函数或过程。
-
将功能封装为方法的目的是:可以实现代码重用,简化代码。
-
Java里面的方法不能独立存在,所有的方法必须定义在类里面。
-
示例:
package com.github.demo1;
/**
* @author open1024
* @version 1.0
* @since 2021-09-01 20:18
*/
public class Person {
private int age;
/**
* 声明方法getAge
*
* @return
*/
public int getAge() {
return this.age;
}
/**
* 声明方法setAage
*
* @param age
*/
public void setAge(int age) {
// 将参数age的值赋给类成员变量age
this.age = age;
}
}
7.2 语法格式
- 格式:
访问修饰符 返回值类型 方法名(参数类型1 参数名1,参数类型2 参数名2,...){
// 方法体程序
return 返回值;
}
-
解释:
-
- 访问修饰符:private、缺省、protected、public等。
-
- 返回值类型:
-
-
- 没有返回值:void。
-
-
-
- 有返回值:声明返回值的类型。和方法体中的
return 返回值 ;
搭配使用。
- 有返回值:声明返回值的类型。和方法体中的
-
-
- 方法名:属于标识符,命名时遵循标识符命名规则和规范,尽量做到
见名知意
。
- 方法名:属于标识符,命名时遵循标识符命名规则和规范,尽量做到
-
- 形参列表:可以包含零个、一个或多个参数。多个参数时,中间用逗号
,
隔开。
- 形参列表:可以包含零个、一个或多个参数。多个参数时,中间用逗号
-
- 返回值:方法在执行完毕后返回给调用它的程序的数据。
7.3 方法的分类
- 按照是否有形参及返回值:
无返回值 | 有返回值 | |
---|---|---|
无形参 | void 方法名(){} | 返回值类型 方法名(){} |
有形参 | void 方法名(形参列表){} | 返回值类型 方法名(形参列表){} |
7.4 方法的调用
- 方法通过方法名被调用,且只有被调用才会执行。
7.5 方法的注意事项
-
① 方法被调用一次,就会执行一次。
-
② 没有具体返回值的情况,返回值类型用关键字
void
表示,那么方法体中可以不使用return语句
;如果使用,仅仅用来结束方法。 -
③ 定义方法时,方法的结果应该返回给调用者,交由调用者处理。
-
④ 方法中只能调用方法或属性,
不可以在方法内部定义方法
。
7.6 练习
7.6.1 练习1
- 创建一个Person类,其定义如下:
-
要求:
-
①创建Person类的对象,设置该对象的name、age和sex属性,调用study方法,输出字符串“studying”,调用showAge()方法显示age值,调用addAge()方法给对象的age属性值增加2岁。
-
②创建第二个对象,执行上述操作,体会同一个类的不同对象之间的关系。
-
示例:
package com.github.exec.demo1;
/**
* @author open1024
* @version 1.0
* @since 2021-09-02 20:42
*/
public class Person {
/**
* 姓名
*/
String name;
/**
* 年龄
*/
int age;
/**
* 性别
*/
int sex;
/**
* 学习
*/
public void study() {
System.out.println("studying");
}
/**
* 显示年龄
*/
public void showAge() {
System.out.println(this.age);
}
/**
* 添加年龄
*
* @param age 年龄
*/
public void addAge(int age) {
this.age += age;
}
@Override
public String toString() {
return "Person{" +
"name='" + this.name + '\'' +
", age=" + this.age +
", sex=" + this.sex +
'}';
}
}
package com.github.exec.demo1;
/**
* @author open1024
* @version 1.0
* @since 2021-09-02 20:54
*/
public class PersonTest {
public static void main(String[] args) {
Person p1 = new Person();
p1.name = "open1024";
p1.age = 20;
p1.sex = 1;
p1.study();
p1.addAge(2);
p1.showAge();
System.out.println("p1 = " + p1);
Person p2 = new Person();
p2.name = "张三";
p2.age = 18;
p2.sex = 2;
p2.study();
p2.addAge(2);
p2.showAge();
System.out.println("p2 = " + p2);
}
}
7.6.2 练习2
-
利用面向对象的编程思想,设计类Circle计算圆的面积。
-
示例:
package com.github.exec.demo2;
/**
* 利用面向对象的编程思想,设计类Circle计算圆的面积
*
* @author open1024
* @version 1.0
* @since 2021-09-02 21:04
*/
public class Circle {
private int radius;
public void setRadius(int radius) {
this.radius = radius;
}
/**
* 计算圆的面积
*
* @return double
*/
public double calculateAreaOfCircle() {
return Math.PI * Math.pow(this.radius, 2);
}
}
package com.github.exec.demo2;
/**
* @author open1024
* @version 1.0
* @since 2021-09-02 21:07
*/
public class CircleTest {
public static void main(String[] args) {
Circle circle = new Circle();
circle.setRadius(2);
double area = circle.calculateAreaOfCircle();
System.out.println("area = " + area);
}
}
8 可变参数
8.1 概述
-
JDK 5 提供了
Varargs
(variable number of arguments)机制,允许直接定义能和多个实参相匹配的形参,从而可以用一种更简单的方式,来传递个数可变的实参。 -
JDK 5 之前采用数组形参来定义方法,传入多个同一类型的变量:
public static void test(int a, String[] args){}
- JDK 5 之后,采用可变个数的形参来定义方法,传入多个同一类型的变量:
public static void test(int a,String... args){}
8.2 注意事项
- ① 语法:
// 参数的类型名,如:int、long、String等
方法名(参数的类型名... 参数名){}
-
② 可变参数:方法参数部分指定类型的参数个数可以是0、1或多个。
-
③ 可变个数形参的方法和同名的方法之间,彼此构成方法重载。
-
④ 可变参数方法的使用和方法参数部分是数组的使用方式是一致的。
-
⑤ 方法的参数部分如果有可变参数,需要放在形参声明的最后。
-
⑥ 在一个方法的形参位置,有且只能有一个可变个数形参。
8.3 应用示例
- 示例:
package com.fairy;
import java.util.Arrays;
/**
* ② 和 ③ 是等同的,如果使用的是IDEA,会帮你将③格式化成②
*
* @author open1024
* @version 1.0
* @since 2021-09-03 14:16
*/
public class VarargsDemo {
public static void main(String[] args) {
Varargs varargs = new Varargs();
// ①
varargs.test("abc");
// ②
varargs.test("bcd", "e", "f");
// ③
varargs.test("cde", new String[]{"g", "h"});
}
}
class Varargs {
public void test(String msg) {
System.out.println("msg = " + msg);
}
public void test(String str, String... msg) {
System.out.println("str = " + str);
System.out.println("msg = " + Arrays.toString(msg));
}
}
9 方法重载(overload)
9.1 概念
- 方法重载:在同一类中,允许存在多个同名方法,只要它们的参数个数或参数类型不同即可。
9.2 特点
-
和返回值类型无关,只看参数列表,且参数列表必须不同(参数个数或参数类型)。
-
调用的时候,根据方法参数列表的不同来区别。
9.3 应用示例
- 示例:
package com.github.overload;
/**
* @author open1024
* @version 1.0
* @since 2021-09-03 10:10
*/
public class Array {
/**
* 返回两个整数的和
*
* @param num1 num1
* @param num2 num2
* @return int
*/
public int add(int num1, int num2) {
return num1 + num2;
}
/**
* 返回三个整数的和
*
* @param num1 num1
* @param num2 num2
* @param num3 num3
* @return int
*/
public int add(int num1, int num2, int num3) {
return num1 + num2 + num3;
}
/**
* 返回两个小数的和
*
* @param num1 num1
* @param num2 num2
* @return double
*/
public double add(double num1, double num2) {
return num1 + num2;
}
}
9.4 方法重载的好处
-
使用方法重载,可以为编程带来方便:让方法的调用者,在调用方法的时候,不需要为了相似的功能去查阅文档,查找各种不同的方法名,降低学习成本,提高开发效率。
-
例如:
System.out.println()
方法就是典型的方法重载,其内部声明如下
public void print(boolean b){}
public void print(char c){}
public void print(int i){}
public void print(long l){}
public void print(float f){}
public void print(double d){}
public void print(char s[]){}
public void print(String s){}
public void print(Object obj){}
public void println(){}
public void println(boolean x){}
public void println(char x){}
public void println(int x){}
public void println(long x){}
public void println(float x){}
public void println(double x){}
public void println(char x[]){}
public void println(String x){}
public void println(Object x){}
9.5 练习
9.5.1 练习1
-
编写程序,定义三个重载方法并调用,方法名为
mOverload
。 -
① 三个方法分别接收一个
int
参数、两个int
参数和一个String
参数,分别执行平方运算并输出结果、乘法运算输出结果、以及输出字符串。 -
② 在主类的
main
方法中分别用参数区别调用三个方法。 -
示例:
package com.github.overload.exec;
/**
* @author open1024
* @version 1.0
* @since 2021-09-03 10:49
*/
public class OverloadDemo1 {
/**
* 执行平方运算并输出结果
*
* @param num
*/
public void mOverload(int num) {
System.out.println((int) Math.pow(num, 2));
}
/**
* 乘法运算并输出结果
*
* @param num1
* @param num2
*/
public void mOverload(int num1, int num2) {
System.out.println(num1 * num2);
}
/**
* 输出字符串信息
*
* @param str str
*/
public void mOverload(String str) {
System.out.println(str);
}
}
package com.github.overload.exec;
/**
* @author open1024
* @version 1.0
* @since 2021-09-03 10:54
*/
public class OverloadDemo1Test {
public static void main(String[] args) {
OverloadDemo1 overloadDemo1 = new OverloadDemo1();
overloadDemo1.mOverload(1);
overloadDemo1.mOverload(1, 2);
overloadDemo1.mOverload("你好,世界");
}
}
9.5.2 练习2
-
定义三个重载方法
max()
,第一个方法求两个int
值中的最大值,第二个方法求两个double
值中的最大值,第三个方法求三个double
值中的最大值,并分别调用这三个方法。 -
示例:
package com.github.overload.exec;
/**
* @author open1024
* @version 1.0
* @since 2021-09-03 10:57
*/
public class ArrayDemo {
/**
* 求两个int值的最大值
*
* @param num1 num1
* @param num2 num2
* @return int
*/
public int max(int num1, int num2) {
return num1 > num2 ? num1 : num2;
}
/**
* 求两个double值的最大值
*
* @param num1 num1
* @param num2 num2
* @return double
*/
public double max(double num1, double num2) {
return num1 > num2 ? num1 : num2;
}
/**
* 求三个double值的最大值
*
* @param num1 num1
* @param num2 num2
* @param num3 num3
* @return double
*/
public double max(double num1, double num2, double num3) {
return num1 > num2 ? (num1 > num3 ? num1 : num3) : (num2 > num3 ? num2 : num3);
}
}
package com.github.overload.exec;
/**
* @author open1024
* @version 1.0
* @since 2021-09-03 11:08
*/
public class ArrayDemoTest {
public static void main(String[] args) {
ArrayDemo demo = new ArrayDemo();
int max = demo.max(1, 2);
System.out.println("max = " + max);
double max1 = demo.max(1.1, 2.2);
System.out.println("max1 = " + max1);
double max2 = demo.max(1.1, 1.2, 1.3);
System.out.println("max2 = " + max2);
}
}
10 方法的值传递机制
10.1 概述
-
方法,必须由其所在类或对象调用才有意义。如果方法有参数:
-
- 形参:方法声明时的参数。
-
- 实参:方法调用的时候实际传递给形参的参数值。
-
那么,Java中的实参值是如何传入方法的?
-
Java里面方法的参数传递方式只有一种:
值传递
。即将实际参数值的副本传入方法内,而参数本身不受影响: -
- 形参是基本数据类型,那么实参将基本数据类型变量的
”数据值“
传递给形参。
- 形参是基本数据类型,那么实参将基本数据类型变量的
-
- 形参是引用数据类型,那么实参将基本数据类型变量的
”地址值“
传递给形参。
- 形参是引用数据类型,那么实参将基本数据类型变量的
10.2 基本数据类型的参数传递
- 示例:
package com.demo1;
/**
* 基本数据类型的参数传递
*
* @author open1024
* @version 1.0
* @since 2021-09-03 14:53
*/
public class TransferParameters {
public static void main(String[] args) {
int x = 5;
System.out.println("修改之前:x=" + x);
change(x);
System.out.println("修改之前:x=" + x);
}
public static void change(int x) {
System.out.println("change修改之前:x=" + x);
x = 3;
System.out.println("change修改之后:x=" + x);
}
}
10.3 引用数据类型的参数传递
- 示例:
package com.demo2;
/**
* 引用数据类型的参数传递
*
* @author open1024
* @version 1.0
* @since 2021-09-03 14:57
*/
public class TransferParameters {
public static void main(String[] args) {
Person p = new Person();
p.age = 5;
System.out.println("修改之前的age=" + p.age); // 5
change(p);
System.out.println("修改之后的age=" + p.age); // 3
}
public static void change(Person p) {
System.out.println("change: 修改之前的age=" + p.age); // 5
p.age = 3;
System.out.println("change: 修改之后的age=" + p.age); // 3
}
}
class Person {
int age;
}
11 递归
11.1 概述
-
递归:一个方法体内调用它自身。
-
方法递归包含了一种隐式的循环,它会重复的执行某段代码,但这种重复执行无需循环控制。
-
递归一定要向已知方向递归,否则这种递归就会变成无穷递归,类似于死循环。
注意:递归需要慎用,一旦递归链过长,会引起内存溢出。
11.2 练习
11.2.1 练习1
-
计算 1 ~ 100 之间的自然数的和。
-
示例:
package com.recursion;
/**
* 计算 1 ~ 100 之间的自然数的和
*
* @author open1024
* @version 1.0
* @since 2021-09-03 15:52
*/
public class Demo1 {
public static void main(String[] args) {
int sum = sum(100);
System.out.println("sum = " + sum);
}
/**
* 递归 计算 1 ~ 100 之间的自然数的和
*
* @param n 自然数
* @return int 和
*/
public static int sum(int n) {
if (n == 1) {
return 1;
}
return n + sum(n - 1);
}
}
11.2.2 练习2
-
使用递归求阶乘
n!
的算法。 -
示例:
package com.recursion;
/**
* 使用递归求阶乘`n!`的算法。 n!= n * (n-1)*...*2*1
*
* @author open1024
* @version 1.0
* @since 2021-09-03 20:17
*/
public class Demo2 {
public static void main(String[] args) {
int result = factorial(5);
System.out.println("result = " + result);
}
/**
* 递归求阶乘`n!`的算法
*
* @param n
* @return
*/
public static int factorial(int n) {
if (n == 1) {
return 1;
}
return n * factorial(n - 1);
}
}
11.2.3 练习3
-
已知有一个数列:f(0) = 1,f(1) = 4,f(n+2)=2*f(n+1) + f(n),其中n是大于0 的整数,求f(10)的值。
-
思路:x=n+2 => f(x)=2*f(n-1)+f(n-2)
-
示例:
package com.recursion;
/**
* @author open1024
* @version 1.0
* @since 2021-09-03 20:22
*/
public class Demo3 {
public static void main(String[] args) {
int result = recursion(0);
System.out.println("result = " + result);
result = recursion(1);
System.out.println("result = " + result);
result = recursion(2);
System.out.println("result = " + result);
result = recursion(10);
System.out.println("result = " + result);
}
public static int recursion(int n) {
if (n == 0) {
return 1;
}
if (n == 1) {
return 4;
}
return 2 * recursion(n - 1) + recursion(n - 2);
}
}
11.2.4 练习4
-
输入一个数据n,计算斐波那契数列(Fibonacci)的第n个值。
-
1 1 2 3 5 8 13 21 34 55。
-
规律:一个数等于前两个数之和。
-
要求:计算斐波那契数列(Fibonacci)的第n个值,并将整个数列打印出来。
-
示例:
package com.recursion;
/**
* 斐波那契数列
*
* @author open1024
* @version 1.0
* @since 2021-09-03 20:34
*/
public class Demo4 {
public static void main(String[] args) {
int result = fibonacci(1);
System.out.println("result = " + result);
result = fibonacci(2);
System.out.println("result = " + result);
result = fibonacci(3);
System.out.println("result = " + result);
result = fibonacci(10);
System.out.println("result = " + result);
}
public static int fibonacci(int n) {
if (n == 1) {
return 1;
}
if (n == 2) {
return 1;
}
return fibonacci(n - 1) + fibonacci(n - 2);
}
}
12 对象数组
12.1 概述
-
数组是用来存储一组数据的容器,一组基本数据类型的数据可以用数组装,那么一组对象也可以使用数组装,换言之,数组中的元素既可以是基本数据类型,也可以是引用数据类型。
-
当数组中的元素是引用数据类型的时候,我们就称之为对象数组。
注意:对象数组,首先要创建数组对象本身,即确定数组的长度,然后再创建每一个元素对象,如果不创建,数组的元素的默认值就是null,很容易出现空指针异常NullPointerException
。
- 示例:
package com.github.array;
/**
* @author open1024
* @version 1.0
* @since 2021-09-02 21:32
*/
public class Person {
/**
* 名称
*/
String name;
/**
* 年龄
*/
int age;
@Override
public String toString() {
return "Person{" +
"name='" + this.name + '\'' +
", age=" + this.age +
'}';
}
}
package com.github.array;
/**
* @author open1024
* @version 1.0
* @since 2021-09-02 21:32
*/
public class PersonTest {
public static void main(String[] args) {
Person p1 = new Person();
p1.name = "李白";
p1.age = 18;
Person p2 = new Person();
p2.name = "杜甫";
p2.age = 20;
Person[] arr = new Person[]{p1, p2};
for (int i = 0; i < arr.length; i++) {
System.out.println(arr[i]);
}
}
}
12.2 练习
-
定义类Student,包含三个属性:学号number(int)、年级grade(int)、成绩score(int)。创建20个学生对象,学号从1到20,年级和成绩由随机数确定。
-
问题一:打印出3年级(grade为3)的学生信息。
-
问题二:使用冒泡排序按学生成绩排序,并遍历所有学生信息。
提示:
-
生成随机数:Math.random(),返回值为boolean类型。
-
冒泡排序:
-
算法思路:
-
- 内层循环(j):
-
-
- 执行范围:
0 ~ (数组长度 - i - 1)
- 执行范围:
-
-
-
- 当前元素和下一个元素进行比较。
-
-
-
- 如果当前元素比下一个元素大,那么就交换两个元素。
-
-
-
- 内层循环执行完毕,会将最大的元素移动到数组的末尾。
-
-
- 外层循环(i):
-
-
- 执行范围:
0 ~ (数组长度-1)
- 执行范围:
-
-
-
- 每执行一次,就将当前范围内的最大元素移动到当前范围内的末尾。
-
-
示例:
package com.github.exec.demo3;
/**
* @author open1024
* @version 1.0
* @since 2021-09-02 21:41
*/
public class Student {
/**
* 学号
*/
int number;
/**
* 年级
*/
int grade;
/**
* 分数
*/
int score;
@Override
public String toString() {
return "Student{" +
"number=" + this.number +
", grade=" + this.grade +
", score=" + this.score +
'}';
}
}
package com.github.exec.demo3;
/**
* @author open1024
* @version 1.0
* @since 2021-09-02 21:41
*/
public class StudentTest {
public static void main(String[] args) {
// 创建Student对象数组,长度为20
Student[] studentArr = new Student[20];
// 遍历对象数组,创建20个Student对象,并存入到数组中
for (int i = 0; i < studentArr.length; i++) {
Student student = new Student();
// 给Student对象设置学号number
student.number = i + 1;
// 给Student对象设置年龄grade
student.grade = (int) (Math.random() * 10) + 1;
// 给Student对象设置分数score
student.score = (int) (Math.random() * 100);
// 框Student对象存入到数组中
studentArr[i] = student;
}
// 打印出3年级(grade为3)的学生信息
for (int i = 0; i < studentArr.length; i++) {
Student student = studentArr[i];
if (student.grade == 3) {
System.out.println("3年级(grade为3)的学生信息 = " + student);
}
}
// 使用冒泡排序按学生成绩排序,并遍历所有学生信息
// 外层循环控制冒泡的次数
for (int i = 0; i < studentArr.length; i++) {
// 内层循环控制当前元素和下一个元素之间的大小
// -i 是因为已经排好序的元素不需要再动
// -1 是因为避免j+1找下一个元素的时候数组越界
for (int j = 0; j < (studentArr.length - i - 1); j++) {
if (studentArr[j].score > studentArr[j + 1].score) {
Student temp = studentArr[j];
studentArr[j] = studentArr[j + 1];
studentArr[j + 1] = temp;
}
}
}
System.out.println("冒泡排序后的数组");
// 输出数组
for (Student student : studentArr) {
System.out.println(student);
}
}
}
13 OOP特征之一:封装
13.1 概述
-
程序设计追求
“高内聚、低耦合”
: -
- 高内聚:类的内部数据操作细节自己完成,不允许外部干涉。
-
- 低耦合:仅对外暴露少量的方法用于使用。
-
封装:隐藏对象内部的复杂性,只对外部公开简单的接口,便于外界调用,从而提高系统的可扩展性、可维护性。换言之,
将该隐藏的隐藏起来,该暴露的暴露出来
。
13.2 为什么要进行封装?
-
使用者对类内部定义的属性(对象的成员变量)的直接操作会导致数据的错误、混乱或安全性问题。
-
示例:
package com.encapsulation.demo1;
/**
* 动物
*
* @author open1024
* @version 1.0
* @since 2021-09-03 22:13
*/
public class Animal {
/**
* 腿
*/
public int legs;
/**
* 吃饭的方法
*/
public void eat() {
System.out.println("吃饭");
}
/**
* 移动的方法
*/
public void move() {
System.out.println("移动");
}
}
package com.encapsulation.demo1;
/**
* @author open1024
* @version 1.0
* @since 2021-09-03 22:14
*/
public class AnimalTest {
public static void main(String[] args) {
Animal animal = new Animal();
// 如果这边赋值的是-100?,不合法啊
// 应该将legs属性保护起来,防止乱用
animal.legs = 4;
System.out.println(animal.legs); // 4
animal.eat();
animal.move();
}
}
13.3 Java中的封装
-
Java中通过将数据声明为私有的(
private
),再提供公共的(public
)方法:setXxx()
和getXxx()
来实现对该属性的操作,以实现以下的目的: -
① 隐藏一个类中不需要对外提供的实现细节。
-
② 使用者只能通过事先定制好的
方法来访问数据
,可以方便的加入控制逻辑,限制对属性的不合理操作。 -
③ 便于修改,增强代码的维护性。
-
示例:
package com.encapsulation.demo2;
/**
* 动物
*
* @author open1024
* @version 1.0
* @since 2021-09-03 22:13
*/
public class Animal {
/**
* 腿 将属性进行私有化
*/
private int legs;
public int getLegs() {
return this.legs;
}
public void setLegs(int legs) {
if (legs != 0 && legs != 2 && legs != 3) {
System.out.println("动物的腿一般为0、2、4");
return;
}
this.legs = legs;
}
/**
* 吃饭的方法
*/
public void eat() {
System.out.println("吃饭");
}
/**
* 移动的方法
*/
public void move() {
System.out.println("移动");
}
}
package com.encapsulation.demo2;
/**
* @author open1024
* @version 1.0
* @since 2021-09-03 22:14
*/
public class AnimalTest {
public static void main(String[] args) {
Animal animal = new Animal();
// 非法 动物的腿一般为0、2、4
animal.setLegs(-100);
System.out.println(animal.getLegs());
animal.eat();
animal.move();
}
}
13.4 四种访问权限修饰符
- Java权限修饰符public、protected、缺省、private置于
类的成员
定义前,用来限定对象对该类成员的访问权限。
修饰符 | 类内部 | 同一个包中 | 不同包的子类 | 同一个工程 |
---|---|---|---|---|
private | √ | |||
缺省 | √ | √ | ||
protected | √ | √ | √ | |
public | √ | √ | √ | √ |
-
对于class的权限修饰符只可以用public和缺省:
-
- public类可以在任意地方被访问。
-
- 缺省类只可以被同一个包内部的类访问。
13.5 练习
-
创建程序,在其中定义两个类:Person和PersonTest。定义如下:用setAge()设置人的合法年龄(0~130),用getAge()返回人的年龄。用PersonTest类中实例化Person类的对象b,调用setAge()和getAge()方法,体会Java的封装性。
-
示例:
package com.encapsulation.demo3;
/**
* @author open1024
* @version 1.0
* @since 2021-09-04 05:45
*/
public class Person {
/**
* 年龄
*/
private int age;
/**
* 获取年龄
*
* @return int
*/
public int getAge() {
return this.age;
}
/**
* 设定年龄
*
* @param age
* 年龄
*/
public void setAge(int age) {
if (age < 0 || age > 130) {
System.out.println("输入不合法");
return;
}
this.age = age;
}
@Override
public String toString() {
return "Person{" + "age=" + this.age + '}';
}
}
package com.encapsulation.demo3;
/**
* @author open1024
* @version 1.0
* @since 2021-09-04 05:47
*/
public class PersonTest {
public static void main(String[] args) {
Person person = new Person();
person.setAge(50);
System.out.println(person.getAge());
System.out.println(person);
}
}
14 类的成员之三:构造器(构造方法)
14.1 概述
- 语法:
访问修饰符 类名 (参数列表){
// 初始化语句;
}
- 构造器的组成:
- 示例:
package com.constructor;
/**
* @author open1024
* @version 1.0
* @since 2021-09-04 06:23
*/
public class Animal {
/**
* 腿
*/
private int legs;
public Animal() {
this.legs = 4;
}
public Animal(int legs) {
this.legs = legs;
}
public int getLegs() {
return this.legs;
}
public void setLegs(int legs) {
this.legs = legs;
}
}
package com.constructor;
/**
* @author open1024
* @version 1.0
* @since 2021-09-04 06:25
*/
public class AnimalTest {
public static void main(String[] args) {
// 创建Animal的实例,调用无参构造器,将legs初始化为4
Animal animal = new Animal();
System.out.println("animal.getLegs() = " + animal.getLegs());
}
}
14.2 构造器的作用
-
① 创建对象。
-
② 给对象进行初始化。
-
示例:
Order o = new Order();
- 示例:
Person p = new Person("张三",15);
14.3 构造器的特征
-
① 构造器具有和类相同的名称。
-
② 构造器不声明返回值类型(和声明void不同)。
-
③ 构造器不能被static、final、synchronized、abstract、native修饰,不能有return语句返回值。
14.4 构造器的分类
-
根据参数的不同,构造器可以分为如下两类:
-
① 隐式无参构造器(系统
默认
提供)。 -
②
显式
定义一个或多个构造器(无参、有参)。
14.5 使用构造器的注意事项
-
① Java语言中,每个类都至少有一个构造器。
-
② 默认构造器的修饰符和所属类的修饰符一致。
-
③ 一旦显式的定义了构造器,则系统
不再
提供默认构造器。 -
④ 一个类可以创建多个
重载
的构造器。 -
⑤ 父类的构造器不可以被子类继承。
14.6 构造器的重载
- 构造器一般用来创建对象的同时初始化对象,如:
class Person{
String name;
int age;
public Person(String name,int age){
this.name = name;
this.age = age;
}
}
- 构造器重载使得创建对象更加灵活,可以很方便的创建不同的对象,如:
class Person{
String name;
int age;
private Date birthday;
public Person(String name,int age){
this.name = name;
this.age = age;
}
public Person(String name,int age,Date birthday){
this(name,age);
this.birthday = birthday;
}
...
}
- 构造器重载,参数列表
必须
不同。
14.7 练习
-
① 定义Student类,有4个属性:
-
- String name;
-
- int age;
-
- String school;
-
- String major;
-
② 定义Student类的3个构造器:
public Student(String name,int age){}
public Student(String name,int age,String school){}
public Student(String name,int age,String school,String major){}
-
③ 在main方法中分别调用不同的构造器创建不同的对象,并输出其属性值。
-
示例:
package com.constructor.demo2;
/**
* @author open1024
* @version 1.0
* @since 2021-09-04 06:49
*/
public class Student {
/**
* 名称
*/
private String name;
/**
* 年龄
*/
private int age;
/**
* 学校
*/
private String school;
/**
* 专业
*/
private String major;
public Student(String name, int age) {
this.name = name;
this.age = age;
}
public Student(String name, int age, String school) {
this(name, age);
this.school = school;
}
public Student(String name, int age, String school, String major) {
this(name, age, school);
this.major = major;
}
@Override
public String toString() {
return "Student{" + "name='" + this.name + '\'' + ", age=" + this.age + ", school='" + this.school + '\''
+ ", major='" + this.major + '\'' + '}';
}
}
package com.constructor.demo2;
/**
* @author open1024
* @version 1.0
* @since 2021-09-04 06:51
*/
public class StudentTest {
public static void main(String[] args) {
Student s1 = new Student("张三", 25);
System.out.println("s1 = " + s1);
Student s2 = new Student("李四", 18, "中国社会大学");
System.out.println("s2 = " + s2);
Student s3 = new Student("王五", 17, "中国社会大学", "萌萌哒专业");
System.out.println("s3 = " + s3);
}
}
14.8 属性赋值总结
-
到目前为止,有很多位置都可以对类的属性进行赋值,现总结这几个位置,并指明赋值的先后顺序。
-
赋值的位置:
-
- ① 默认初始化。
-
- ② 显示初始化。
-
- ③ 构造器中初始化。
-
- ④ 通过
“对象.属性“
或”对象.方法“
的方式赋值。
- ④ 通过
-
赋值的先后顺序:① → ② → ③ → ④
15 JavaBean
15.1 概述
-
JavaBean是一种Java语言编写的可重用组件。
-
所谓JavaBean,是指符合如下标准的Java类:
-
- ①
类是公共的
。
- ①
-
- ②
有一个无参的公共的构造器
。
- ②
-
- ③
有属性,且有对应的getter和setter方法
。
- ③
-
用户可以使用JavaBean将功能、处理、值、数据库访问和其他任何可以用Java代码创建的对象进行打包,并且其他的开发者可以通过内部的Jsp页面、Servle、其他JavaBean或者应用来使用这些对象。
-
用户可以认为JavaBean提供了一种随时随地的复制和粘贴的功能,而不用关系任何改变。
15.2 应用示例
- 示例:
package com.javabean;
/**
* @author open1024
* @version 1.0
* @since 2021-09-04 08:17
*/
public class Person {
/**
* 名称
*/
private String name;
/**
* 年龄
*/
private int age;
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return this.age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Person{" + "name='" + this.name + '\'' + ", age=" + this.age + '}';
}
}
16 关键字 this
16.1 概述
-
在Java中,this关键字比较难理解,它的作用和其词义比较接近:
-
- ① 它在方法内部使用,表示这个方法所属对象的引用。
-
- ② 它在构造器内部使用,表示该构造器正在初始化的对象。
-
this可以调用类的属性、方法和构造器。
-
当在方法内部需要用到调用该方法的对象是,就需要使用this,具体来说,就是用this来区分
属性
和局部变量
。
16.2 this调用属性和方法
-
① 在任意方法或构造器内,如果使用当前类的成员变量或成员方法可以在前面添加this,增强程序的阅读性。不过,通常情况下,我们都习惯省略this。
-
② 当形参和成员变量同名的时候,如果在方法内部或构造器内部需要使用成员变量,必须添加this来表明该变量是类的成员变量。
-
③ 使用this访问属性和方法时,如果在本类中没有找到,会从父类中查找。
-
示例:
package top.open1024.demo;
/**
* @author open1024
* @version 1.0
* @since 2021-09-04 09:11
*/
public class Person {
/**
* 姓名
*/
private final String name;
/**
* 年龄
*/
private final int age;
/**
* 构造方法
*
* @param name 姓名
* @param age 年龄
*/
public Person(String name, int age) {
this.name = name;
this.age = age;
}
/**
* 获取信息
*/
public void getInfo() {
System.out.println("姓名:" + this.name);
this.speak();
}
/**
* 说话
*/
private void speak() {
System.out.println("年龄:" + this.age);
}
}
评论区