1、面向对象的概述及两大要素:类与对象
1. 面向对象内容的三条主线:
- Java类及类的成员:(重点)属性、方法、构造器;(熟悉)代码块、内部类
- 面向对象的特征:封装、继承、多态、(抽象)
- 其他关键字的使用:this、super、package、import、static、final、interface、abstract等
2. 面向过程编程(POP) vs 面向对象编程(OOP)
2.1 简单的语言描述二者的区别
> 面向过程:
- 以`函数`为组织单位。
- 是一种“`执行者思维`”,适合解决简单问题。扩展能力差、后期维护难度较大。
> 面向对象:
- 以`类`为组织单位。每种事物都具备自己的`属性`和`行为/功能`。
- 是一种“`设计者思维`”,适合解决复杂问题。代码扩展性强、可维护性高。
2.2 二者关系:
我们千万不要把面向过程和面向对象对立起来。他们是相辅相成的。面向对象离不开面向过程!
3.
> 面向对象编程的两个核心概念:类(Class)、对象(Object)
> 谈谈对这两个概念的理解?
类:具有相同特征的事物的抽象描述,是`抽象的`、概念上的定义。
对象:实际存在的该类事物的`每个个体`,是`具体的`,因而也称为`实例(instance)`。
2、类的实例化与对象的内存解析
类的声明与使用
1、体会:设计类,其实就是设计类的成员
class Person{
}
2、类的内部成员一、二:
成员之一:属性、成员变量、field(字段、域)
成员之二:(成员)方法、函数、method
3、类的实例化
等价描述:类的实例化 <=> 创建类的对象 <=> 创建类的实例
格式:类类型 对象名 = 通过new创建的对象实体
举例:
Phone p1 = new Phone();
Scanner scan = new Scanner(System.in);
String str = new String();
面向对象完成具体功能的操作的三步流程(非常重要)
步骤1:创建类,并设计类的内部成员(属性、方法)
步骤2:创建类的对象。比如:Phone p1 = new Phone();
步骤3:通过对象,调用其内部声明的属性或方法,完成相关的功能
package com.atguigu01.oop;
/**
* @author 尚硅谷-宋红康
* @create 16:26
*/
public class Phone {
//属性
String name;//品牌
double price;//价格
//方法
public void call(){
System.out.println("手机能够拨打电话");
}
public void sendMessage(String message){
System.out.println("发送信息:" + message);
}
public void playGame(){
System.out.println("手机可以玩游戏");
}
}
package com.atguigu01.oop;
/**
* @author 尚硅谷-宋红康
* @create 16:29
*/
public class PhoneTest { //是Phone类的测试类
public static void main(String[] args) {
//复习:数据类型 变量名 = 变量值
// Scanner scann = new Scanner(System.in);
//创建Phone的对象
Phone p1 = new Phone();
//通过Phone的对象,调用其内部声明的属性或方法
//格式:"对象.属性" 或 "对象.方法"
p1.name = "huawei mate50";
p1.price = 6999;
System.out.println("name = " + p1.name + ", price = " + p1.price);
//调用方法
p1.call();
p1.sendMessage("有内鬼,终止交易");
p1.playGame();
}
}
2. 类中对象的内存解析
2.1 创建类的一个对象
见《01-创建类的一个对象.png》
2.2 创建类的多个对象
见《02-创建类的多个对象1.png》、《02-创建类的多个对象2.png》
强调1:创建了Person类的两个对象
Person p1 = new Person();
Person p2 = new Person();
说明:创建类的多个对象时,每个对象在堆空间中有一个对象实体。每个对象实体中保存着一份类的属性。
如果修改某一个对象的某属性值时,不会影响其他对象此属性的值。
p1.age = 10;
p2.age = 20;
p1.age = 30;
System.out.println(p2.age);//20
强调2:声明类的两个变量
Person p1 = new Person();
Person p3 = p1;
说明:此时的p1,p3 两个变量指向了堆空间中的同一个对象实体。(或p1,p3保存的地址值相同)
如果通过其中某一个对象变量修改对象的属性时,会影响另一个对象变量此属性的值。
p1.age = 10;
p3.age = 20;
System.out.println(p1.age);//20
package com.atguigu02.memory;
/**
* @author 尚硅谷-宋红康
* @create 14:31
*/
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);
}
}
package com.atguigu02.memory;
/**
* Perosn类对应的测试类
*
* @author 尚硅谷-宋红康
* @create 14:38
*/
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);
}
}
3、局部变量与成员变量的对比及练习
类的成员之一:属性
1.变量的分类:
- 角度一:按照数据类型来分:基本数据类型(8种)、引用数据类型(数组、类、接口、枚举、注解、记录)
- 角度二:按照变量在类中声明的位置的不同:成员变量(或属性)、局部变量(方法内、方法形参、构造器内、构造器形参、代码块内等)
2. 属性的几个称谓:成员变量、属性、field(字段、域)
3. 区分成员变量 vs 局部变量
3.1 相同点:
> 变量声明的格式相同:数据类型 变量名 = 变量值
> 变量都有其有效的作用域。出了作用域,就失效了。
> 变量必须先声明,后赋值,再使用。
3.2 不同点:
① 类中声明的位置的不同:
属性:声明在类内,方法外的变量
局部变量:声明方法、构造器内部的变量
② 在内存中分配的位置不同(难):
属性:随着对象的创建,存储在堆空间中。
局部变量:存储在栈空间中
③ 生命周期:
属性:随着对象的创建而创建,随着对象的消亡而消亡。
局部变量:随着方法对应的栈帧入栈,局部变量会在栈中分配;随着方法对应的栈帧出栈,局部变量消亡。
④ 作用域:
属性:在整个类的内部都是有效的
局部变量:仅限于声明此局部变量所在的方法(或构造器、代码块)中
⑤ 是否可以有权限修饰符进行修饰:(难)
都有哪些权限修饰符:public、protected、缺省、private。(用于表明所修饰的结构可调用的范围的大小)
属性,是可以使用权限修饰符进行修饰的。 暂时还未讲封装性,所以大家先不用写任何权限符。
而局部变量,不能使用任何权限修饰符进行修饰的。
⑥ 是否有默认值:(重点)
属性:都有默认初始化值
意味着,如果没有给属性进行显式初始化赋值,则会有默认初始化值。
局部变量:都没有默认初始化值。
意味着,在使用局部变量之前,必须要显式的赋值,否则报错。
注意:对于方法的形参而言,在调用方法时,给此形参赋值即可。
package com.atguigu03.field_method.field;
/**
* @author 尚硅谷-宋红康
* @create 20:07
*/
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);
}
}
案例1:
声明员工类Employee,包含属性:编号id、姓名name、年龄age、薪资salary。
声明EmployeeTest测试类,并在main方法中,创建2个员工对象,并为属性赋值,并打印两个员工的信息。
package com.atguigu03.field_method.field.exer1;
/**
* ClassName: Employee
* Description:
* 声明员工类Employee,包含属性:编号id、姓名name、年龄age、薪资salary。
*
* 声明EmployeeTest测试类,并在main方法中,创建2个员工对象,并为属性赋值,并打印两个员工的信息。
*
* @Author 尚硅谷-宋红康
* @Create 8:51
* @Version 1.0
*/
public class Employee {
//属性(或成员变量)
int id; //编号
String name; //姓名
int age; //年龄
double salary; //薪资
}
package com.atguigu03.field_method.field.exer1;
/**
* ClassName: EmployeeTest
* Description:
*
* @Author 尚硅谷-宋红康
* @Create 8:51
* @Version 1.0
*/
public class EmployeeTest {
public static void main(String[] args) {
//创建类的实例(或创建类的对象、类的实例化)
Employee emp1 = new Employee();
System.out.println(emp1);//类型@地址值
emp1.id = 1001;
emp1.name = "Tom";
emp1.age = 24;
emp1.salary = 7800;
System.out.println("id = " + emp1.id + ",name = " + emp1.name +
", age = " + emp1.age + ", salary = " + emp1.salary);
//创建Employee的第2个对象
// Employee emp3 = emp1;//错误写法
Employee emp2 = new Employee();
System.out.println("id = " + emp2.id + ",name = " + emp2.name +
", age = " + emp2.age + ", salary = " + emp2.salary);
}
}
案例2:
(1)声明一个MyDate类型,有属性:年year,月month,日day
(2)声明一个Employee类型,包含属性:编号、姓名、年龄、薪资、生日(MyDate类型)
(3)在EmployeeTest测试类中的main()中,创建两个员工对象,并为他们的姓名和生日赋值,并显示
package com.atguigu03.field_method.field.exer2;
/**
* ClassName: MyDate
* Description:
* 声明一个MyDate类型,有属性:年year,月month,日day
* @Author 尚硅谷-宋红康
* @Create 9:03
* @Version 1.0
*/
public class MyDate {
int year; //年
int month; //月
int day; //日
}
package com.atguigu03.field_method.field.exer2;
/**
* ClassName: Employee
* Description:
* 声明一个Employee类型,包含属性:编号、姓名、年龄、薪资、生日(MyDate类型)
* @Author 尚硅谷-宋红康
* @Create 9:03
* @Version 1.0
*/
public class Employee {
int id; //编号
String name; //姓名
int age; //年龄
double salary; //薪资
MyDate birthday; //生日
}
package com.atguigu03.field_method.field.exer2;
/**
* ClassName: EmployeeTest
* Description:
*
* @Author 尚硅谷-宋红康
* @Create 9:07
* @Version 1.0
*/
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.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 + "日]");
}
}
4、方法的作用与方法的声明
类的成员之二:方法(method)
1. 使用方法的好处
方法的理解:`方法`是类或对象行为特征的抽象,用来完成某个功能操作。
方法的好处:实现代码重用,减少冗余,简化代码
2. 使用举例
- Math.random()的random()方法
- Math.sqrt(x)的sqrt(x)方法
- System.out.println(x)的println(x)方法
- new Scanner(System.in).nextInt()的nextInt()方法
- Arrays类中的binarySearch()方法、sort()方法、equals()方法
3. 声明举例
public void eat()
public void sleep(int hour)
public String interests(String hobby)
public int getAge()
4. 方法声明的格式 (重要)
权限修饰符 [其它修饰符] 返回值类型 方法名(形参列表) [throws 异常类型]{ //方法头
//方法体
}
注:[]中的内部不是必须的,以后再讲。
5. 具体的方法声明的细节
5.1 权限修饰符
① Java规定了哪些权限修饰符呢? 有四种:private \ 缺省 \ protected \ public (放到封装性讲)
暂时大家声明方法时,可以先都写成public的。
5.2 返回值类型:描述当调用完此方法时,是否需要返回一个结果。
分类:
> 无返回值类型:使用void表示即可。比如:System.out.println(x)的println(x)方法、Arrays的sort()
> 有具体的返回值类型:需要指明返回的数据的类型。可以是基本数据类型,也可以引用数据类型
> 需要在方法内部配合使用"return + 返回值类型的变量或常量"
比如:Math.random()、new Scanner(System.in).nextInt()等
[经验]我们在声明方法时,要不要提供返回值类型呢?
> 根据方法具体实现的功能来决定。换句话说,具体问题具体分析
> 根据题目要求
5.3 方法名:属于标识符。需要满足标识符的规定和规范。“见名知意”
5.4 形参列表:形参,属于局部变量,且可以声明多个。
格式:(形参类型1 形参1,形参类型2 形参2,...)
分类:无形参列表 、 有形参列表
> 无形参列表:不能省略一对()。比如:Math.random()、new Scanner(System.in).nextInt()
> 有形参列表:根据方法调用时,需要的不确定的变量的类型和个数,确定形参的类型和个数。
比如:Arrays类中的binarySearch()方法、sort()方法、equals()方法
[经验]我们在声明方法时,是否需要形参列表呢?
> 根据方法具体实现的功能来决定。换句话说,具体问题具体分析
> 根据题目要求
5.5 方法体: 当我们调用一个方法时,真正执行的代码。体现了此方法的功能。
6. 注意点
> Java里的方法`不能独立存在`,所有的方法必须定义在类里。
> Java中的方法不调用,不执行。每调用一次,就执行一次。
> 方法内可以调用本类中的(其它)方法或属性
> 方法内不能定义方法。
7.关键字:return
7.1 return的作用
- 作用1:结束一个方法
- 作用2:结束一个方法的同时,可以返回数据给方法的调用者 (方法声明中如果有返回值类型,则方法内需要搭配return使用)
7.2 使用注意点:
return后面不能声明执行语句。
8. 方法调用的内存解析:
- 形参:方法在声明时,一对()内声明的一个或多个形式参数,简称为形参。
- 实参:方法在被调用时,实际传递给形参的变量或常量,就称为实际参数,简称实参。
package com.atguigu03.field_method.method;
/**
* @author 尚硅谷-宋红康
* @create 19:01
*/
public class MethodTest {
public static void main(String[] args) {
Person p1 = new Person();
p1.eat();
p1.eat();
p1.info();
p1.sleep(8);
String info = p1.interests("编程");
System.out.println(info);
}
}
class Person{
//属性
String name;
int age;
char gender;
//方法
public void eat(){
System.out.println("人吃饭");
sleep(8);
System.out.println("name = " + name);
}
public void sleep(int hour){
System.out.println("人至少每天睡眠" + hour + "小时");
}
public String interests(String hobby){
String info = "我的爱好是" + hobby;
System.out.println(info);
// return info;
return "abc";
}
public int getAge(){
return age;
}
public void info(){
System.out.println("Person info()");
// info();
//方法内不能定义方法!
// public void show(){
//
// }
}
public void printNumber(int targetNumber){ //10
for(int i = 1;i <= targetNumber;i++){
if(i == 4){
return ; //用于结束方法。
//return后面不能声明执行语句
// System.out.println("迪丽热巴要约我吃饭");
}
System.out.println(i);
}
}
}
5、属性和方法的整体练习1-4
案例1:
(1)创建Person类的对象,设置该对象的name、age和gender属性,
调用study方法,输出字符串“studying”;
调用showAge()方法,返回age值;
调用addAge(int addAge)方法给对象的age属性值增加addAge岁。比如:2岁。
(2)创建第二个对象,执行上述操作,体会同一个类的不同对象之间的关系。
package com.atguigu04.example.exer1;
/**
* ClassName: Person
* Description:
* 创建Person类的对象,设置该对象的name、age和gender属性,
* 调用study方法,输出字符串“studying”;
* 调用showAge()方法,返回age值;
* 调用addAge(int addAge)方法给对象的age属性值增加addAge岁。比如:2岁。
*
* @Author 尚硅谷-宋红康
* @Create 11:42
* @Version 1.0
*/
public class Person {
String name;
int age;
char gender;
public void study(){
System.out.println("studying");
}
public int showAge(){
return age;
}
public void addAge(int addAge){
age += addAge;
}
}
package com.atguigu04.example.exer1;
/**
* ClassName: PersonTest
* Description:
*
* 创建Person类的对象,设置该对象的name、age和gender属性,
* 调用study方法,输出字符串“studying”;
* 调用showAge()方法,返回age值;
* 调用addAge(int addAge)方法给对象的age属性值增加addAge岁。比如:2岁。
*
* 创建第二个对象,执行上述操作,体会同一个类的不同对象之间的关系。
*
* @Author 尚硅谷-宋红康
* @Create 11:43
* @Version 1.0
*/
public class PersonTest {
public static void main(String[] args) {
Person p1 = new Person();
//调用属性
p1.name = "Tom";
p1.age = 12;
p1.gender = '男';
//调用方法
p1.study();
p1.addAge(2);
int age = p1.showAge();
System.out.println("age = " + age);//12 --> 14
Person p2 = new Person();
System.out.println(p2.age);//0
p2.addAge(10);
System.out.println(p2.showAge());//10
System.out.println(p1.showAge());//14
}
}
案例2:
1. 编写程序,声明一个method1方法,在方法中打印一个10*8 的*型矩形,在main方法中调用该方法。
2. 编写程序,声明一个method2方法,除打印一个10*8的*型矩形外,
再计算该矩形的面积,并将其作为方法返回值。在main方法中调用该方法,接收返回的面积值并打印。
3. 编写程序,声明一个method3方法,在method3方法提供m和n两个参数,方法中打印一个m*n的*型矩形,
并计算该矩形的面积,将其作为方法返回值。在main方法中调用该方法,接收返回的面积值并打印。
package com.atguigu04.example.exer2;
/**
* ClassName: Exer02
* Description:
* 1. 编写程序,声明一个method1方法,在方法中打印一个10*8 的*型矩形,在main方法中调用该方法。
*
* 2. 编写程序,声明一个method2方法,除打印一个10*8的*型矩形外,
* 再计算该矩形的面积,并将其作为方法返回值。在main方法中调用该方法,接收返回的面积值并打印。
*
* 3. 编写程序,声明一个method3方法,在method3方法提供m和n两个参数,方法中打印一个m*n的*型矩形,
* 并计算该矩形的面积,将其作为方法返回值。在main方法中调用该方法,接收返回的面积值并打印。
*
*
* @Author 尚硅谷-宋红康
* @Create 11:50
* @Version 1.0
*/
public class Exer02 {
public void method1(){
for (int i = 0; i < 10; i++) {
for (int j = 0; j < 8; j++) {
System.out.print("*");
}
System.out.println();
}
}
public int method2(){
for (int i = 0; i < 10; i++) {
for (int j = 0; j < 8; j++) {
System.out.print("*");
}
System.out.println();
}
return 10 * 8;
}
public int method3(int m ,int n){
for (int i = 0; i < m; i++) {
for (int j = 0; j < n; j++) {
System.out.print("*");
}
System.out.println();
}
return m * n;
}
}
package com.atguigu04.example.exer2;
/**
* ClassName: Exer02Test
* Description:
*
* @Author 尚硅谷-宋红康
* @Create 11:54
* @Version 1.0
*/
public class Exer02Test {
public static void main(String[] args) {
//创建Exer02的对象
Exer02 exer = new Exer02();
// exer.method1();
// int area = exer.method2();
// System.out.println("面积为:" + area);
int area1 = exer.method3(3,8);
System.out.println("面积为:" + area1);
}
}
案例3:
利用面向对象的编程方法,设计类Circle计算圆的面积。
package com.atguigu04.example.exer3;
/**
* ClassName: Circle
* Description:
* 利用面向对象的编程方法,设计类Circle计算圆的面积。
* @Author 尚硅谷-宋红康
* @Create 14:04
* @Version 1.0
*/
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);
// }
}
package com.atguigu04.example.exer3;
/**
* ClassName: CircleTest
* Description:
*
* @Author 尚硅谷-宋红康
* @Create 14:07
* @Version 1.0
*/
public class CircleTest {
public static void main(String[] args) {
Circle c1 = new Circle();
c1.radius = 2.3;
c1.findArea();
System.out.println("面积为:" + c1.findArea2());
}
}
案例4:
根据上一章数组中的常用算法操作,自定义一个操作int[]的工具类。
涉及到的方法有:求最大值、最小值、总和、平均数、遍历数组、复制数组、数组反转、
数组排序(默认从小到大排序)、查找等
package com.atguigu04.example.exer4;
/**
* ClassName: MyArrays
* Description:
* 根据上一章数组中的常用算法操作,自定义一个操作int[]的工具类。
* 涉及到的方法有:求最大值、最小值、总和、平均数、遍历数组、复制数组、数组反转、
* 数组排序(默认从小到大排序)、查找等
* @Author 尚硅谷-宋红康
* @Create 14:13
* @Version 1.0
*/
public class MyArrays {
/**
* 获取int[]数组的最大值
* @param arr 要获取最大值的数组
* @return 数组的最大值
*/
public 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;
}
/**
* 获取int[]数组的最小值
* @param arr 要获取最小值的数组
* @return 数组的最小值
*/
public int getMin(int[] arr){
int min = arr[0];
for (int i = 1; i < arr.length; i++) {
if(min > arr[i]){
min = arr[i];
}
}
return min;
}
public int getSum(int[] arr){
int sum = 0;
for (int i = 0; i < arr.length; i++) {
sum += arr[i];
}
return sum;
}
public int getAvg(int[] arr){
return getSum(arr) / arr.length;
}
public void print(int[] arr){ //[12,231,34]
System.out.print("[");
for (int i = 0; i < arr.length; i++) {
if(i == 0){
System.out.print(arr[i]);
}else{
System.out.print("," + arr[i]);
}
}
System.out.println("]");
}
public int[] copy(int[] arr){
int[] newArr = new int[arr.length];
for (int i = 0; i < arr.length; i++) {
newArr[i] = arr[i];
}
return newArr;
}
public void reverse(int[] arr){
for(int i = 0,j = arr.length - 1;i < j;i++,j--){
//交互arr[i] 与 arr[j]位置的元素
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
}
public void sort(int[] arr){
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;
}
}
}
}
/**
* 使用线性查找的算法,查找指定的元素
* @param arr 待查找的数组
* @param target 要查找的元素
* @return target元素在arr数组中的索引位置。若未找到,则返回-1
*/
public int linearSearch(int[] arr,int target){
for(int i = 0;i < arr.length;i++){
if(target == arr[i]){
return i;
}
}
//只要代码执行到此位置,一定是没找到
return -1;
}
}
package com.atguigu04.example.exer4;
/**
* ClassName: MyArraysTest
* Description:
*
* @Author 尚硅谷-宋红康
* @Create 14:16
* @Version 1.0
*/
public class MyArraysTest {
public static void main(String[] args) {
MyArrays arrs = new MyArrays();
int[] arr = new int[]{34,56,223,2,56,24,56,67,778,45};
//求最大值
System.out.println("最大值为:" + arrs.getMax(arr));
//求平均值
System.out.println("平均值为:" + arrs.getAvg(arr));
//遍历
arrs.print(arr);
//查找
int index = arrs.linearSearch(arr,24);
if(index >= 0){
System.out.println("找到了,位置为:" + index);
}else{
System.out.println("未找到");
}
//排序
arrs.sort(arr);
//遍历
arrs.print(arr);
}
}
6、对象数组的使用及内存解析
对象数组
1. 何为对象数组?如何理解?
数组的元素可以是基本数据类型,也可以是引用数据类型。当元素是引用类型中的类时,我们称为对象数组。
2. 举例:
String[],Person[],Student[],Customer[]等
案例:
1)定义类Student,包含三个属性:学号number(int),年级state(int),成绩score(int)。
2)创建20个学生对象,学号为1到20,年级和成绩都由随机数确定。
问题一:打印出3年级(state值为3)的学生信息。
问题二:使用冒泡排序按学生成绩排序,并遍历所有学生信息
提示:
1) 生成随机数:Math.random(),返回值类型double;
2) 四舍五入取整:Math.round(double d),返回值类型long。
年级[1,6] : (int)(Math.random() * 6 + 1)
分数[0,100] : (int)(Math.random() * 101)
public class Student {
//属性
int number;//学号
int state;//年级
int score;//成绩
//声明一个方法,显示学生的属性信息
public String show(){
return "number : " + number + ",state : " +
state + ", score : " + score;
}
}
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)的学生信息
StudentUtil util = new StudentUtil();
util.printStudentsWithState(students,3);
//需求2:使用冒泡排序按学生成绩排序,并遍历所有学生信息
//排序前遍历
util.printStudents(students);
System.out.println("********************");
util.sortStudents(students);
//排序后遍历
util.printStudents(students);
}
}
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;
}
}
}
}
}
7、方法的重载
再谈方法之1:方法的重载(overload)
1. 定义:在同一个类中,允许存在一个以上的同名方法,只要它们的参数列表不同即可。
满足这样特征的多个方法,彼此之间构成方法的重载。
2. 总结为:“两同一不同”
两同:同一个类、相同的方法名
一不同:参数列表不同。① 参数个数不同 ② 参数类型不同
注意:方法的重载与形参的名、权限修饰符、返回值类型都没有关系。
3. 举例
Arrays类中sort(xxx[] arr)、binarySearch(xxx[] arr,xxx)、equals(xxx[] ,yyy[])
4. 如何判断两个方法是相同的呢?(换句话说,编译器是如何确定调用的某个具体的方法呢?)
如何判断两个方法是相同的呢? 方法名相同,且形参列表相同。(形参列表相同指的是参数个数和类型都相同,与形参名没关系)
要求:在一个类中,允许存在多个相同名字的方法,只要他们的形参列表不同即可。
编译器是如何确定调用的某个具体的方法呢?先通过方法名确定了一波重载的方法,进而通过不同的形参列表,确定具体的某一个方法。
5. 在同一个类中不允许定义两个相同的方法。
public class OverloadTest {
public static void main(String[] args) {
OverloadTest test = new OverloadTest();
test.add(1,2,3);
test.add(10,20);
test.add(10,20.0);
}
public void add(int i,int j){
System.out.println("1111");
}
public void add(double d1,double d2){
System.out.println("3333");
}
public void add(int i,int j,int k){
}
public void add(String s1,String s2){
}
public void add(int i,String s){
}
public void add(String s,int i){
}
// public void add(int m ,int n){
// System.out.println("2222");
// }
// public int add(int m,int n){
// return m + n;
// }
}
练习1:判断与void show(int a,char b,double c){}构成重载的有:
a)void show(int x,char y,double z){} //no
b)int show(int a,double c,char b){} //yes
c) void show(int a,double c,char b){} //yes
d) boolean show(int c,char b){} //yes
e) void show(double c){} //yes
f) double show(int x,char y,double z){} //no
g) void shows(){double c} //no
练习2:
编写程序,定义三个重载方法并调用。方法名为mOL。
三个方法分别接收一个int参数、两个int参数、一个字符串参数。
分别执行平方运算并输出结果,相乘并输出结果,输出字符串信息。
练习3:
定义三个重载方法max():
第一个方法求两个int值中的最大值,
第二个方法求两个double值中的最大值,
第三个方法求三个double值中的最大值,并分别调用三个方法。
public class OverLoadExer {
// 练习2:
// 编写程序,定义三个重载方法并调用。方法名为mOL。
// 三个方法分别接收一个int参数、两个int参数、一个字符串参数。
// 分别执行平方运算并输出结果,相乘并输出结果,输出字符串信息。
public void mOL(int num){
System.out.println(num * num);
}
public void mOL(int num1,int num2){
System.out.println(num1 * num2);
}
public void mOL(String message){
System.out.println(message);
}
// 练习3:
// 定义三个重载方法max():
// 第一个方法求两个int值中的最大值,
// 第二个方法求两个double值中的最大值,
// 第三个方法求三个double值中的最大值,并分别调用三个方法。
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;
}
}
7、方法的重载
1. 定义:在同一个类中,允许存在一个以上的同名方法,只要它们的参数列表不同即可。
满足这样特征的多个方法,彼此之间构成方法的重载。
2. 总结为:“两同一不同”
两同:同一个类、相同的方法名
一不同:参数列表不同。① 参数个数不同 ② 参数类型不同
注意:方法的重载与形参的名、权限修饰符、返回值类型都没有关系。
3. 举例
Arrays类中sort(xxx[] arr)、binarySearch(xxx[] arr,xxx)、equals(xxx[] ,yyy[])
4. 如何判断两个方法是相同的呢?(换句话说,编译器是如何确定调用的某个具体的方法呢?)
如何判断两个方法是相同的呢? 方法名相同,且形参列表相同。(形参列表相同指的是参数个数和类型都相同,与形参名没关系)
要求:在一个类中,允许存在多个相同名字的方法,只要他们的形参列表不同即可。
编译器是如何确定调用的某个具体的方法呢?先通过方法名确定了一波重载的方法,进而通过不同的形参列表,确定具体的某一个方法。
5. 在同一个类中不允许定义两个相同的方法。
package com.atguigu05.method_more._01overload;
/**
* ClassName: OverloadTest
* Description:
*
* @Author 尚硅谷-宋红康
* @Create 16:13
* @Version 1.0
*/
public class OverloadTest {
public static void main(String[] args) {
OverloadTest test = new OverloadTest();
test.add(1,2,3);
test.add(10,20);
test.add(10,20.0);
}
public void add(int i,int j){
System.out.println("1111");
}
public void add(double d1,double d2){
System.out.println("3333");
}
public void add(int i,int j,int k){
}
public void add(String s1,String s2){
}
public void add(int i,String s){
}
public void add(String s,int i){
}
// public void add(int m ,int n){
// System.out.println("2222");
// }
// public int add(int m,int n){
// return m + n;
// }
}
练习1:判断与void show(int a,char b,double c){}构成重载的有:
a)void show(int x,char y,double z){} //no
b)int show(int a,double c,char b){} //yes
c) void show(int a,double c,char b){} //yes
d) boolean show(int c,char b){} //yes
e) void show(double c){} //yes
f) double show(int x,char y,double z){} //no
g) void shows(){double c} //no
练习2:
编写程序,定义三个重载方法并调用。方法名为mOL。
三个方法分别接收一个int参数、两个int参数、一个字符串参数。
分别执行平方运算并输出结果,相乘并输出结果,输出字符串信息。
练习3:
定义三个重载方法max():
第一个方法求两个int值中的最大值,
第二个方法求两个double值中的最大值,
第三个方法求三个double值中的最大值,并分别调用三个方法。
public class OverLoadExer {
// 练习2:
// 编写程序,定义三个重载方法并调用。方法名为mOL。
// 三个方法分别接收一个int参数、两个int参数、一个字符串参数。
// 分别执行平方运算并输出结果,相乘并输出结果,输出字符串信息。
public void mOL(int num){
System.out.println(num * num);
}
public void mOL(int num1,int num2){
System.out.println(num1 * num2);
}
public void mOL(String message){
System.out.println(message);
}
// 练习3:
// 定义三个重载方法max():
// 第一个方法求两个int值中的最大值,
// 第二个方法求两个double值中的最大值,
// 第三个方法求三个double值中的最大值,并分别调用三个方法。
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;
}
}
8、可变个数形参的方法
再谈方法之2:可变个数形参的方法(jdk5.0)
1. 使用场景
在调用方法时,可能会出现方法形参的类型是确定的,但是参数的个数不确定。此时,我们就可以使用可变个数形参的方法
2. 格式:(参数类型 ... 参数名)
3. 说明:
① 可变个数形参的方法在调用时,针对于可变的形参赋的实参的个数可以为:0个、1个或多个
② 可变个数形参的方法与同一个类中,同名的多个方法之间可以构成重载
③ 特例:可变个数形参的方法与同一个类中方法名相同,且与可变个数形参的类型相同的数组参数不构成重载。
④ 可变个数的形参必须声明在形参列表的最后
⑤ 可变个数的形参最多在一个方法的形参列表中出现一次
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});
// 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,int ... nums){
}
//编译不通过
// public void print(int ... nums,int i){
//
// }
public void print(int i){
System.out.println("2222");
}
public void print(int i,int j){
System.out.println("3333");
}
/*
场景举例:
* String sql = "update customers set name = ?,email = ? where id = ?";
*
* String sql1 = "update customers set name = ? where id = ?";
*
* public void update(String sql,Object ... objs);
*
* */
}
练习:可变形参的方法
n个字符串进行拼接,每一个字符串之间使用某字符进行分割,如果没有传入字符串,那么返回空字符串""
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;
}
}
8、方法值传递机制的剖析
再谈方法之3:方法的值传递机制
1. (复习)对于方法内声明的局部变量来说:如果出现赋值操作
> 如果是基本数据类型的变量,则将此变量保存的数据值传递出去。
> 如果是引用数据类型的变量,则将此变量保存的地址值传递出去。
2. 方法的参数的传递机制:值传递机制
2.1 概念(复习)
形参:在定义方法时,方法名后面括号()中声明的变量称为形式参数,简称形参。
实参:在调用方法时,方法名后面括号()中的使用的值/变量/表达式称为实际参数,简称实参。
2.2 规则:实参给形参赋值的过程
> 如果形参是基本数据类型的变量,则将实参保存的数据值赋给形参。
> 如果形参是引用数据类型的变量,则将实参保存的地址值赋给形参。
3. 面试题:Java中的参数传递机制是什么?值传递。(不是引用传递)
public class ValueTransferTest1 {
public static void main(String[] args) {
ValueTransferTest1 test = new ValueTransferTest1();
//1. 对于基本数据类型的变量来说
int m = 10;
test.method1(m);
System.out.println("m = " + m);//10
//2. 对于引用数据类型的变量来说
Person p = new Person();
p.age = 10;
test.method2(p);
System.out.println(p.age); //11
}
public void method1(int m){
m++;
}
public void method2(Person p){
p.age++;
}
}
class Person{
int age;
}
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);
}
public void swap(int m ,int n){
int temp = m;
m = n;
n = temp;
}
}
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;
}
题目
1. 定义一个Circle类,包含一个double型的radius属性代表圆的半径,一个findArea()方法返回圆的面积。
2. 定义一个类PassObject,在类中定义一个方法printAreas(),该方法的定义如下:
public void printAreas(Circle c, int time)。
3. 在printAreas方法中打印输出1到time之间的每个整数半径值,以及对应的面积。
例如,time为5,则输出半径1,2,3,4,5,以及对应的圆面积。
4. 在main方法中调用printAreas()方法,调用完毕后输出当前半径值。程序运行结果如图所示。
public class PassObject {
public static void main(String[] args) {
PassObject obj = new PassObject();
Circle circle = new Circle();
obj.printAreas(circle,5);
System.out.println("now radius is:" +circle.radius);
}
public void printAreas(Circle c, int time){
System.out.println("Radius\t\tArea");
int i = 1;
for (; i <= time; i++) {
c.radius = i;
System.out.println(c.radius + "\t\t\t" + c.findArea());
}
c.radius = i;
}
}
public class Circle {
double radius;
public double findArea() {
return Math.PI * radius * radius;
}
}
9、递归方法
再谈方法之4:递归方法
1. 何为递归方法?方法自己调用自己的现象就称为递归。
2. 递归方法分类:直接递归、间接递归
3. 使用说明:
- 递归方法包含了一种`隐式的循环`。
- 递归方法会`重复执行`某段代码,但这种重复执行无须循环控制。
- 递归一定要向`已知方向`递归,否则这种递归就变成了无穷递归,停不下来,类似于`死循环`。最终发生`栈内存溢出`。
注意:
1. 递归调用会占用大量的系统堆栈,内存耗用多,在递归调用层次多时速度要比循环`慢的多`,
所以在使用递归时要慎重。
2. 在要求高性能的情况下尽量避免使用递归,递归调用既花时间又`耗内存`。考虑使用循环迭代
public class RecursionTest {
public static void main(String[] args) {
RecursionTest test = new RecursionTest();
// test.method1();
System.out.println(test.getSum(100));
System.out.println(test.getSum1(100));
int n = 5;
System.out.println(test.getMul(5)); //
int m = 10;
System.out.println(test.f(m));
}
/*
* 举例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;
}
}
/*
* 举例2:计算n!
* */
public int getMul(int n){
if(n == 1){
return 1;
}else{
return n * getMul(n - 1);
}
}
/*
* 举例3:快速排序
* */
/*
* 举例4:汉诺塔游戏
* */
/*
* 举例5:斐波那契数列
* 1 1 2 3 5 8 13 21 34 55 ...
*
* f(n) = f(n - 1) + f(n - 2)
* */
public int f(int n){
if(n == 1){
return 1;
}else if(n == 2){
return 1;
}else{
return f(n - 1) + f(n - 2);
}
}
/*
* 举例6:
* File类的对象表示一个文件目录。
* 计算指定的文件目录的大小,遍历指定的文件目录中的所有的文件,删除指定的文件目录。
* */
/*
* 如下递归方法的调用,会导致StackOverflowError。
* */
// public void method1(){
// System.out.println("method1()....");
// method1();
// }
}
案例:不死神兔
用递归实现不死神兔:故事得从西元1202年说起,话说有一位意大利青年,名叫斐波那契(Fibonacci)。
在他的一部著作中提出了一个有趣的问题:假设一对刚出生的小兔一个月后就能长成大兔,
再过一个月就能生下一对小兔,并且此后每个月都生一对小兔,没有发生死亡,
问:现有一对刚出生的兔子2年后(24个月)会有多少对兔子?
月份 1 2 3 4 5
兔子对数 1 1 2 3 5
public class RabbitExer {
public static void main(String[] args) {
RabbitExer exer = new RabbitExer();
int month = 24;
System.out.println("兔子的对数为:" + exer.getRabbitNumber(month));
}
public int getRabbitNumber(int month){
if(month == 1){
return 1;
}else if(month == 2){
return 1;
}else{
return getRabbitNumber(month - 1) + getRabbitNumber(month - 2);
}
}
}
拓展:走台阶问题
假如有10阶楼梯,小朋友每次只能向上走1阶或者2阶,请问一共有多少种不同的走法呢?
阶数 1 2 3 4 。。。
走法 1 2 3 5 。。。
从n为3开始:
f(n) = f(n - 1) + f(n - 2)
【奇妙的属性】随着数列的增加,斐波那契数列前一个数与后一个数的比值越来越逼近黄金分割的数值0.618。
10、package与import
一、package关键字的使用
1. 说明
- package:包
- package用于指明该文件中定义的类、接口等结构所在的包
- 一个源文件只能有一个声明包的package语句
- package语句作为Java源文件的第一条语句出现。若缺省该语句,则指定为无名包。
- 包名,属于标识符,满足标识符命名的规则和规范(全部小写)、见名知意
- 包通常使用所在公司域名的倒置:com.atguigu.xxx。
- 大家取包名时不要使用"`java.xx`"包
- 包对应于文件系统的目录,package语句中用 “.” 来指明包(目录)的层次,每.一次就表示一层文件目录。
- 同一个包下可以声明多个结构(类、接口),但是不能定义同名的结构(类、接口)。不同的包下可以定义同名的结构(类、接口)
2. 包的作用
- 包可以包含类和子包,划分`项目层次`,便于管理
- 帮助`管理大型软件`系统:将功能相近的类划分到同一个包中。比如:MVC的设计模式
- 解决`类命名冲突`的问题
- 控制`访问权限`
3. 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)。
二、import关键字的使用
- import : 导入
- import语句来显式引入指定包下所需要的类。相当于`import语句告诉编译器到哪里去寻找这个类`。
- import语句,声明在包的声明和类的声明之间。
- 如果需要导入多个类或接口,那么就并列显式多个import语句即可
- 如果使用`a.*`导入结构,表示可以导入a包下的所有的结构。举例:可以使用java.util.*的方式,一次性导入util包下所有的类或接口。
- 如果导入的类或接口是java.lang包下的,或者是当前包下的,则可以省略此import语句。
- 如果已经导入java.a包下的类,那么如果需要使用a包的子包下的类的话,仍然需要导入。
- 如果在代码中使用不同包下的同名的类,那么就需要使用类的全类名的方式指明调用的是哪个类。
- (了解)`import static`组合的使用:调用指定类或接口下的静态的属性或方法
package com.atguigu06.package_import;
//import java.util.ArrayList;
//import java.util.HashMap;
//import java.util.Scanner;
import com.atguigu05.method_more._04recursion.RecursionTest;
import com.atguigu05.method_more._04recursion.exer2.RabbitExer;
import java.lang.reflect.Field;
import java.util.*;
import static java.lang.System.out;
import static java.lang.Math.PI;
/**
* ClassName: PackageImportTest
* Description:
*
* @Author 尚硅谷-宋红康
* @Create 11:13
* @Version 1.0
*/
public class PackageImportTest {
public static void main(String[] args) {
Scanner scan = new Scanner(System.in);
ArrayList list = new ArrayList();
HashMap map = new HashMap();
HashSet set = new HashSet();
String str = "hello";
System.out.println(str);
Person p = new Person();
Field field = null;
RecursionTest test = null;
RabbitExer exer = null;
//Date可以使用import的方式指明
Date date = new Date();
//使用全类名的方式
java.sql.Date date1 = new java.sql.Date(121231231L);
out.println("hello");
out.println(PI);
}
}
11、封装性
面向对象特征之一:封装性
1. 为什么需要封装性?
理论上:
-`高内聚`:类的内部数据操作细节自己完成,不允许外部干涉;
-`低耦合`:仅暴露少量的方法给外部使用,尽量方便外部调用。
通俗的说:把该隐藏的隐藏起来,该暴露的暴露出来。这就是封装性的设计思想。
2. 如何实现数据封装?
2.1 权限修饰符
Java规定了4种权限修饰,分别是:private、缺省、protected、public
2.2 作用
我们可以使用4种权限修饰来修饰类及类的内部成员。当这些成员被调用时,体现可见性的大小。
2.3 实际案例:
在题目中,我们给Animal的对象的legs属性赋值。在实际的常识中,我们知道legs不能赋值为负数的。但是如果
直接调用属性legs,是不能加入判断逻辑的。那怎么办呢?
> 将legs属性私有化(private),禁止在Animal类的外部直接调用此属性
> 提供给legs属性赋值的setLegs()方法,在此方法中加入legs赋值的判断逻辑if(legs >= 0 && legs % 2 ==0)
将此方法暴露出去,使得在Animal类的外部调用此方法,对legs属性赋值。
> 提供给legs属性获取的getLegs()方法,此方法对外暴露。使得在Animal类的外部还可以调用此属性的值。
2.4 4种权限具体使用
《见课件》
> 类:只能使用public、缺省修饰
> 类的内部成员:可以使用4种权限修饰进行修饰。
2.5 开发中4种权限使用频率的情况:
比较高:public、private
比较低:缺省、protected
3. 封装性的体现
> 场景1:私有化(private)类的属性,提供公共(public)的get和set方法,对此属性进行获取或修改
> 场景2:将类中不需要对外暴露的方法,设置为private.
> 场景3:单例模式中构造器private的了,避免在类的外部创建实例。(放到static关键字后讲)
12、封装性的课后练习与几种不同权限修饰符的测试
案例1:
创建程序,在其中定义两个类:Person和PersonTest类。定义如下:
用setAge()设置人的合法年龄(0~130),用getAge()返回人的年龄。
在PersonTest类中实例化Person类的对象b,调用setAge()和getAge()方法,体会Java的封装性。
public class Person {
private int age;
//设置age属性
public void setAge(int a){
if(a >= 0 && a <= 130){
age = a;
}else{
System.out.println("你输入的数据非法");
}
}
//获取age属性
public int getAge(){
return age;
}
//错误的
// public int doAge(int a){
// if(a >= 0 && a <= 130){
// age = a;
// return age;
// }else{
// System.out.println("你输入的数据非法");
// return -1;
// }
// }
}
public class PersonTest {
public static void main(String[] args) {
//创建Person实例1
Person p1 = new Person();
// p1.age = 10; //编译不通过
// System.out.println(p1.age);
p1.setAge(20);
System.out.println(p1.getAge());
}
}
案例2:自定义图书类
设定属性包括:
书名bookName,
作者author,
价格price;
方法包括:
相应属性的get/set方法,
图书信息介绍等。
public class Book {
private String bookName; //书名
private String author; //作者
private double price; //价格
public String getBookName() {
return bookName;
}
public void setBookName(String bn) {
bookName = bn;
}
public String getAuthor() {
return author;
}
public void setAuthor(String a) {
author = a;
}
public double getPrice() {
return price;
}
public void setPrice(double p) {
price = p;
}
//获取读书信息
public String showInfo() {
return "bookName : " + bookName + ", author : " + author + ", price : " + price;
}
}
public class BookTest {
public static void main(String[] args) {
Book book1 = new Book();
book1.setBookName("剑指Java");
book1.setAuthor("尚硅谷教育");
book1.setPrice(180.0);
System.out.println(book1.showInfo());
}
}
案例3:普通员工类
(1)声明员工类Employee,
- 包含属性:姓名、性别、年龄、电话,属性私有化
- 提供get/set方法
- 提供String getInfo()方法
(2)在测试类的main中创建员工数组,并从键盘输入员工对象信息,最后遍历输出
public class Employee {
//属性
private String name;
private char gender;
private int age;
private String phoneNumber;
//提供属性的get和set方法
public void setName(String n) {
name = n;
}
public String getName() {
return name;
}
public void setGender(char g) {
gender = g;
}
public char getGender() {
return gender;
}
public void setAge(int a) {
age = a;
}
public int getAge() {
return age;
}
public void setPhoneNumber(String pn) {
phoneNumber = pn;
}
public String getPhoneNumber() {
return phoneNumber;
}
public String getInfo(){
// return name + "\t" + gender + "\t" + age + "\t" + phoneNumber;
return getName() + "\t" + getGender() + "\t" + getAge() + "\t" + getPhoneNumber();
}
}
public class EmployeeTest {
public static void main(String[] args) {
Scanner scan = new Scanner(System.in);
//创建Employee[]
Employee[] emps = new Employee[2];
for (int i = 0; i < emps.length; i++) {
emps[i] = new Employee();
System.out.println("----------添加第" + (i + 1) + "个员工----------");
System.out.print("姓名:");
String name = scan.next();
System.out.print("性别:");
char gender = scan.next().charAt(0);
System.out.print("年龄:");
int age = scan.nextInt();
System.out.print("电话:");
String phoneNumber = scan.next();
//给指定的employee对象的各属性赋值
emps[i].setName(name);
emps[i].setGender(gender);
emps[i].setAge(age);
emps[i].setPhoneNumber(phoneNumber);
}
//遍历员工列表
System.out.println("---------------员工列表------------------");
System.out.println("编号\t姓名\t性别\t年龄\t电话");
for (int i = 0; i < emps.length; i++) {
System.out.println((i + 1) + "\t" + emps[i].getInfo());
}
System.out.println("---------------员工列表完成------------------");
}
}
权限修饰符的访问范围
13、构造器的使用与练习
类的成员之三:构造器(constructor),构造方法
1. 构造器的理解
constructor :n. 建设者、建造者
construct:v. 建设、建造、创造
construction: n. 建设、建造 CCB
2. 构造器的作用
作用1:搭配new关键字,创建类的对象
作用2:在创建对象的同时,可以给对象的相关属性赋值
3. 构造器的使用说明
> 构造器声明的格式:权限修饰符 类名(形参列表){}
> 创建类以后,在没有显示提供任何构造器的情况下,系统会默认提供一个空参的构造器,且构造器的权限与类声明的权限相同。
> 一旦类中显示声明了构造器,则系统不再提供默认的空参的构造器。
> 一个类中可以声明多个构造器,彼此之间构成重载。
class Person {
//属性
String name;
int age;
//构造器
public Person(){
System.out.println("Person()....");
}
//声明其它的构造器
public Person(int a){
age = a;
}
public Person(String n){
name = n;
}
public Person(String n,int a){
name = n;
age = a;
}
//方法
public void eat(){
System.out.println("人吃饭");
}
public void sleep(int hour){
System.out.println("每天睡眠" + hour + "小时");
}
}
public class PersonTest {
public static void main(String[] args) {
Person p1 = new Person(1);
p1.eat();
}
}
案例1:
(1)定义Student类,有4个属性:
String name;
int age;
String school;
String major;
(2)定义Student类的3个构造器:
- 第一个构造器Student(String n, int a)设置类的name和age属性;
- 第二个构造器Student(String n, int a, String s)设置类的name, age 和school属性;
- 第三个构造器Student(String n, int a, String s, String m)设置类的name, age ,school和major属性;
(3)在main方法中分别调用不同的构造器创建的对象,并输出其属性值。
public class Student {
String name;
int age;
String school;
String major;//专业
public Student(String n,int a){
name = n;
age = a;
}
public Student(String n, int a, String s){
name = n;
age = a;
school = s;
}
Student(String n, int a, String s, String m){
name = n;
age = a;
school = s;
major = m;
}
public String getInfo(){
return "name = " + name + ",age = " + age +
",school = " + school + ", major = " + major;
}
}
public class StudentTest {
public static void main(String[] args) {
Student s1 = new Student("刘强东",48,"中国人民大学","社会学");
System.out.println(s1.getInfo());
Student s2 = new Student("奶茶妹妹",28,"清华大学");
System.out.println(s2.getInfo());
}
}
案例2:
编写两个类,TriAngle和TriAngleTest,其中TriAngle类中声明私有的底边长base和高height,
同时声明公共方法访问私有变量。此外,提供类必要的构造器。另一个类中使用这些公共方法,计算三角形的面积。
public class TriAngle {
//属性
private double base;//底边长
private double height;//高
public double getBase(){
return base;
}
public void setBase(double b){
base = b;
}
public double getHeight(){
return height;
}
public void setHeight(double h){
height = h;
}
public TriAngle(){
}
public TriAngle(double b,double h){
base = b;
height = h;
}
//...
//求面积的方法
public double findArea(){
return base * height / 2;
}
}
public class TriAngleTest {
public static void main(String[] args) {
//创建TriAngle的实例1
TriAngle t1 = new TriAngle();
t1.setHeight(2.3);
t1.setBase(3.4);
System.out.println("面积为:" + t1.findArea());
//创建TriAngle的实例2
TriAngle t2 = new TriAngle(2.4,4.5);
System.out.println("底边长为:" + t2.getBase());
System.out.println("高为:" + t2.getHeight());
System.out.println("面积为:" + t2.findArea());
}
}
案例3:
1、写一个名为Account的类模拟账户。该类的属性和方法如下图所示。
该类包括的属性:账号id,余额balance,年利率annualInterestRate;
包含的构造器:自定义
包含的方法:访问器方法(getter和setter方法),取款方法withdraw(),存款方法deposit()。
提示:在提款方法withdraw中,需要判断用户余额是否能够满足提款数额的要求,如果不能,应给出提示。
public class Account {
private int id;//账号
private double balance; //余额
private double annualInterestRate;//年利率
public Account(int i, double b, double a) {
id = i;
balance = b;
annualInterestRate = a;
}
public void setId(int i) {
id = i;
}
public int getId() {
return id;
}
public void setBalance(double b) {
balance = b;
}
public double getBalance() {
return balance;
}
public void setAnnualInterestRate(double a) {
annualInterestRate = a;
}
public double getAnnualInterestRate() {
return annualInterestRate;
}
//取钱
public void withdraw(double amount){
if(amount <= balance && amount > 0){
balance -= amount;
System.out.println("成功取出:" + amount);
}else{
System.out.println("余额不足,取款失败");
}
}
//存款
public void deposit(double amount){
if(amount > 0){
balance += amount;
System.out.println("成功存入:" + amount);
}
}
}
2、创建Customer类。
a. 声明三个私有对象属性:firstName、lastName和account。
b. 声明一个公有构造器,这个构造器带有两个代表对象属性的参数(f和l)
c. 声明两个公有存取器来访问该对象属性,方法getFirstName和getLastName返回相应的属性。
d. 声明setAccount 方法来对account属性赋值。
e. 声明getAccount 方法以获取account属性。
public class Customer {
private String firstName;
private String lastName;
private Account account;
public Customer(String f,String l){
firstName = f;
lastName = l;
}
public String getFirstName(){
return firstName;
}
public String getLastName(){
return lastName;
}
public void setAccount(Account a){
account = a;
}
public Account getAccount(){
return account;
}
}
3、写一个测试程序。
(1)创建一个Customer ,名字叫 Jane Smith, 他有一个账号为1000,余额为2000元,年利率为 1.23% 的账户。
(2)对Jane Smith操作。
存入 100 元,再取出960元。再取出2000元。
打印出Jane Smith 的基本信息:
public class CustomerTest {
public static void main(String[] args) {
//创建Customer实例
Customer customer = new Customer("Jane","Smith");
// Account account = new Account(1000,2000,0.0123);
// customer.setAccount(account);
//或
customer.setAccount(new Account(1000,2000,0.0123));
//针对于客户的账户进行取钱、存钱的操作
customer.getAccount().deposit(100);
customer.getAccount().withdraw(960);
customer.getAccount().withdraw(2000);
//输出客户信息
//Customer [Smith, Jane] has a account: id is 1000,
// annualInterestRate is 1.23%, balance is 1140.0
System.out.println("Customer [" + customer.getLastName() + "," + customer.getFirstName() +
"] has a account:id is " + customer.getAccount().getId() + ",annualInterestRate is " +
customer.getAccount().getAnnualInterestRate()*100 + "%,balance is " +
customer.getAccount().getBalance());
/*
* 关于匿名对象
*
* 1. 匿名对象往往只能被调用一次
* 2. 匿名对象常常作为实参传递给方法的形参。
* */
new Account(1001,2000,0.0123).withdraw(1000);
System.out.println(new Account(1001, 2000, 0.0123).getBalance());
int num = 10;
}
}
成功存入 :100.0
成功取出:960.0
余额不足,取款失败
Customer [Smith, Jane] has a account: id is 1000, annualInterestRate is 1.23%, balance is 1140.0
14、实例变量赋值过程JavaBean_UML类图
一、类中属性(当前仅考虑实例变量)赋值过程:
1. 在类的属性中,可以有哪些位置给属性赋值?
① 默认初始化;
② 显式初始化;
③ 构造器中初始化;
**********************************
④ 通过"对象.方法"的方式赋值;
⑤ 通过"对象.属性"的方式赋值;
2. 这些位置执行的先后顺序是怎样?
① - ② - ③ - ④/⑤
3. 以上操作在对象创建过程中可以执行的次数如何?
> 只能执行一次:①、②、③
> 可以多次执行:④、⑤
二、JavaBean的理解
所谓JavaBean,是指符合如下标准的Java类:
- 类是公共的
- 有一个无参的公共的构造器
- 有属性,且有对应的get、set方法
三、读懂UML类图
public class Customer {
public Customer(){
}
private int id;
private String name;
public void setId(int i){
id = i;
}
public int getId(){
return id;
}
public void setName(String n){
name = n;
}
public String getName(){
return name;
}
}
public class UserTest {
public static void main(String[] args) {
User u1 = new User();
System.out.println(u1.age);
User u2 = new User(2);
System.out.println(u2.age);
u2.age = 3;
u2.age = 4;
}
}
class User{
//属性(或实例变量)
String name;
int age = 10;
public User(){
// age = 20;
}
public User(int a){
age = a;
}
}