day09复习
1. 练习
/**
* 声明一个日期类型MyDate:有属性:年year,月month,日day。
*
* 创建2个日期对象,分别赋值为:你的出生日期,你对象的出生日期,并显示信息。
*
*
* 小结:
* 基本数据类型的变量就根据其类型存储相应的值。比如:int i = 5; char c = 'a'
* 引用数据类型的变量存储的值就两种情况:① null ② 对象或数组实体在堆空间中存储位置的首地址值。
*
* @author shkstart
* @create 9:25
*/
class MyDate{
int year;
int month;
int day;
public String getInfo(){
return "对象的信息为:year = " + year + ",month = " + month + ",day = " + day;
}
public void show(){
System.out.println("对象的信息为:year = " + year + ",month = " + month + ",day = " + day);
}
public String show1(){
System.out.println("对象的信息为:year = " + year + ",month = " + month + ",day = " + day);
return null;
}
}
public class MyDateTest { //测试MyDate类
public static void main(String[] args) {
MyDate myDate1 = new MyDate();
myDate1.year = 2021;
myDate1.month = 3;
myDate1.day = 8;
System.out.println(myDate1.getInfo());
MyDate myDate2 = myDate1;//不能称为重新创建了一个对象!
MyDate myDate3 = new MyDate();
myDate3.year = 2021;
myDate3.month = 11;
myDate3.day = 11;
System.out.println(myDate3.getInfo());
//创建多个MyDate类对象构成的数组
MyDate[] arr = new MyDate[5];
System.out.println(arr);//[Lcom.atguigu.exer.MyDate;@1540e19d
for(int i = 0;i < arr.length;i++){
arr[i] = new MyDate();
//System.out.println(arr[i]);//com.atguigu.exer.MyDate@各个地址值
//正确的写法
// System.out.println(arr[i].getInfo());
//正确的写法
arr[i].show();
//正确的写法
System.out.println(arr[i].show1());
//错误的写法
// System.out.println(arr[i].show());
}
}
}
2. 方法的参数传递机制:值传递
2.1 方法内局部变量的值传递机制
class Order{
int orderId;
}
public class ValueTransferTest {
public static void main(String[] args) {
//针对基本数据类型的变量:存储就是变量的值
int m = 10;
int n = m;
System.out.println("n = " + n);//10
m = 20;
System.out.println("n = " + n);//10
System.out.println("################");
//针对于引用数据类型的变量:存储的数据:① null ② 对象或数组实体在堆空间中存储位置的首地址值。
Order o1 = new Order();
o1.orderId = 1001;
// o1 = null;
Order o2 = o1;
System.out.println(o2.orderId);//1001
o1.orderId = 1002;
System.out.println(o2.orderId);//1002
}
}
结论:
/**
- 复习:
- 针对基本数据类型的变量:存储就是变量的值
- 针对于引用数据类型的变量:存储的数据:① null ② 对象或数组实体在堆空间中存储位置的首地址值。
*- 2.总结:值传递机制
- 基本数据类型变量的赋值操作:就是将基本数据类型变量存储的变量值赋值过去。
- 引用数据类型变量的赋值操作:就是将引用数据类型变量存储的null值或者对象的首地址值赋值过去。
*
*
*
*/
2.2 方法形参的传递机制
- 形参是基本数据类型:
public class ValueTransferTest1 {
public static void main(String[] args) {
int m = 10;
int n = 20;
System.out.println("m = " + m +", n = " + n);
//交换两个变量的值
//方式一:成功交换了
// int temp = m;
// m = n;
// n = temp;
//方式二:失败!没有实现交换
ValueTransferTest1 test = new ValueTransferTest1();
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 ValueTransferTest2 {
public static void main(String[] args) {
Data d1 = new Data();
d1.m = 10;
d1.n = 20;
System.out.println("m = " + d1.m + ", n = " + d1.n);
//交换两个变量:
ValueTransferTest2 test = new ValueTransferTest2();
test.swap(d1);
System.out.println("m = " + d1.m + ", n = " + d1.n);
}
public void swap(Data data){
int temp = data.m;
data.m = data.n;
data.n = temp;
}
}
class Data{
int m;
int n;
}
内存解析:
结论:
* 1.概念: * 形参:方法声明时,小括号内的参数 * 实参:方法调用时,实际传递给形参变量的值 * * * 2. 方法形参的传递机制:值传递! * 如果形参是基本数据类型的变量,则将实参基本数据类型变量存储的变量值赋值给形参。 * 如果形参是引用数据类型的变量,则将实参引用数据类型变量存储的null值或者对象的首地址值赋值给形参。
3. 递归方法
package com.atguigu.java1;
/**
* 递归方法的说明:
*
* 1. 概念:一个方法体内调用它自身,就构成了递归方法的调用。
*
* 2. 递归方法的调用,要避免出现 StackOverflowError异常。
* 此情况类似于死循环。
*
* 3. 递归一定要向已知方向递归,否则这种递归就变成了无穷递归,类似于死循环。
*
* @author shkstart
* @create 11:07
*/
public class RecursionTest {
public static void main(String[] args) {
RecursionTest test = new RecursionTest();
// test.show();//StackOverflowError异常
System.out.println(test.sum(100));
System.out.println(test.sum1(100));
System.out.println(test.multiply(10));
System.out.println(test.func(10));
System.out.println(test.fei(10));
}
public void show(){
System.out.println("递归调用");
show();
}
//举例1:求100以内自然数的和
//不使用递归方法
public int sum(int num){
int sum = 0;
for(int i = 1;i <= num;i++){
sum += i;
}
return sum;
}
public int sum1(int num){
if(num == 1){
return 1;
}else{
return sum1(num - 1) + num;
}
}
//举例2:求n!
public int multiply(int n){
if(n == 1){
return 1;
}else{
return multiply(n - 1) * n;
}
}
//举例3:已知有一个数列:f(0) = 1,f(1) = 4,f(n+2) = 2*f(n+1) + f(n),
//其中n是大于0的整数,求f(10)的值。
public int f(int n){
if(n == 0){
return 1;
}else if(n == 1){
return 4;
}else{
return 2 * f(n - 1) + f(n - 2);
}
}
//举例4:已知一个数列:f(20) = 1,f(21) = 4,f(n+2) = 2*f(n+1)+f(n),
//其中n是大于0的整数,求f(10)的值。
public int func(int m){
if(m == 20){
return 1;
}else if(m == 21){
return 4;
}else{
return func(m + 2) - 2 * func(m + 1);
// return 2 * func(m - 1) + func(m - 2);//错误的
}
}
//举例5:斐波那契数列: 1 1 2 3 5 8 13 21 34 55 ...
//f(1) = 1,f(2) = 1, f(n) = f(n - 1) + f(n - 2)
//走n级台阶,每次只能走一个台阶或两个台阶。问n级台阶有多少种走法?
public int fei(int n){
if(n == 1){
return 1;
}else if(n == 2){
return 1;
}else {
return fei(n - 1) + fei(n - 2);
}
}
//举例6:汉诺塔
//举例7:通过递归遍历文件目录,获取文件目录的大小(或删除文件目录)
//举例8:快速排序
}
4. oop的特征一:封装性
4.1 封装性的体现之一
class Animal{//动物类
private int legs;//腿的个数 当使用private修饰以后,表示只能在此类内部使用
//针对legs属性的设置
public void setLegs(int leg){
if(leg < 0 || leg % 2 != 0){
System.out.println("输入的数据非法!");
}else{
legs = leg;
}
}
//针对legs属性的获取
public int getLegs(){
return legs;
}
private int age;
//set方法
public void setAge(int a){
age = a;
}
//get方法
public int getAge(){
return age;
}
//不要将get()和set()合并!
// public int doAge(int a){
// age = a;
// return age;
// }
}
* 1. 封装性的理解:把该隐藏的隐藏起来,该暴露的暴露出来。这就是封装性的设计思想。 * 比如:私有化(private)类的属性,不让在类的外部随意调用。并且提供公共的(public)方法对此属性进行访问。我们可以在方法内编写对属性赋值等操作的限制。 * 2. 对属性通过方法的访问,我们可以体现为:获取、设置。
4.2 封装性的理解
* 3. 对封装性的理解:
* 一方面,我们在设计功能时,将不同的功能封装进不同的方法,将方法声明在相关的类中。
* 另一方面,Java提供了几种权限修饰符用于修饰类及类的内部成员。体现这些结构在被调用时的可见性的大小。
*
* 4. 封装性的体现:
* 举例1:私有化(private)类的属性,提供公共的(public)方法进行获取、设置。
* 举例2:设计类时,如果有的方法不需要对外暴露,只在内部使用,则可以声明为private的
* 举例3:单例模式(后面讲)
4.3 权限修饰符
* 5. Java规范的几种权限修饰:从小到大的顺序为:
* private < 缺省 < protected < public
*
* 可以使用如上的4种权限修饰来修饰类的内部成员;
* 针对于类来讲,只能使用public、缺省的权限进行修饰。
5. 类的成员之三:构造器
- 使用说明
* 类的成员之三:构造器(或构造方法、Constructor)
*
* 1. 构造器的作用:
* ① 配合+new关键字在一起,用于创建类的对象
* ② 在创建对象的同时,初始化对象的相关属性
*
*
* 2. 构造器使用说明:
* ① 如果我们在定义类时,没有显式的声明类的构造器,则系统会默认给我们提供一个空参的构造器
* 当我们显式的定义了类的构造器时,系统就不再给我们提供默认的空参的构造器了
* ② 构造器的声明格式:
* 权限修饰符 类名(形参列表){
* 构造器的体
* }
* ③ 一个类中可以声明多个构造器,彼此之间构成重载
- 代码演示
class Person{
//属性
String name;
int age;
//构造器
public Person(){
System.out.println("我是Person类的空参的构造器");
}
public Person(int a){
age = a;
}
public Person(String n,int a){
name = n;
age = a;
}
//方法
public void eat(){
System.out.println("人吃饭");
}
public void sleep(){
System.out.println("人睡觉");
}
public void think(String message){
System.out.println("人思考:" + message);
}
}
public class PersonTest {
public static void main(String[] args) {
// Scanner scann = new Scanner(System.in);
Person p1 = new Person();
// p1.age = 1;
// p1.name = "Tom";
// p1.sleep();
// p1.think("我从哪里来的?");
Person p2 = new Person(10);
System.out.println(p2.age);
Person p3 = new Person("Jerry",20);
System.out.println("name :" + p3.name + ", age :" + p3.age);
}
}
- 课后练习
package com.atguigu.exer1;
/**
* 创建程序,在其中定义两个类:Person和PersonTest类。定义如下:
* 用setAge()设置人的合法年龄(0~130),用getAge()返回人的年龄。
*
* 1. 在前面定义的Person类中添加构造器,利用构造器设置所有人的age属性初始值都为18。
*
* 2. 修改上题中类和构造器,增加name属性,使得每次创建Person对象的同时初始化对象的age属性值
* 和name属性值。
*
* @author shkstart
* @create 14:46
*/
public class Person {
private int age;
private String name;
public Person(){
age = 18;
}
public Person(String n,int a){
name = n;
age = a;
}
public void setAge(int i){
if(i >= 0 && i <= 130){
age = i;
}
}
public int getAge(){
return age;
}
public void setName(String n){
name = n;
}
public String getName(){
return name;
}
}
public class PersonTest {
public static void main(String[] args) {
Person b = new Person();
System.out.println(b.getAge());
// b.age = 10;
b.setAge(10);
System.out.println(b);//com.atguigu.exer1.Person@1540e19d
System.out.println(b.getAge());
System.out.println("############");
Person p1 = new Person("Tom",12);
System.out.println("name = " + p1.getName() + ", age = " + p1.getAge());
}
}
6. 类中属性赋值的先后顺序
/**
* 类中属性赋值的位置:
*
* ① 属性的默认赋值(或默认初始化)
* ② 属性的显式赋值(或显式初始化)
* ③ 构造器中赋值(或在构造中初始化)
*
*
* ④ 通过"对象.属性" 或 "对象.方法"的方式,赋值
*
* 赋值的先后顺序:
* ① - ② - ③ - ④
*
* 说明:
* 上述的① ② ③ 三个步骤在初始化过程中,只执行一次!
*
*
*
* @author shkstart
* @create 15:30
*/
public class UserTest {
public static void main(String[] args) {
User u1 = new User(10);
System.out.println(u1.age);
u1.age = 20;
u1.age = 30;
System.out.println(u1.age);
}
}
class User{
String name;
int age = 1;
public User(){
}
public User(int a){
age = a;
}
}
7. JavaBean 、UML类图
8. this关键字的使用
8.1 this调用属性、方法
class Person{
//属性
private String name;
private int age;
//构造器
public Person(){
}
public Person(int age){
this.age = age;
this.eat();
}
public Person(String name,int age){
this.name = name;
this.age = age;
}
//方法
public void eat(){
System.out.println("人吃饭");
// this.sleep();
sleep();
}
public void sleep(){
System.out.println("人睡觉");
}
public void think(String message){
System.out.println("人思考:" + message);
}
public void setName(String name){
this.name = name;
}
public String getName(){
return name;
}
public void setAge(int age){
this.age = age;
}
public int getAge(){
return age;
}
public void show(){
System.out.println("name = " + this.name + ",age = " + age);
}
}
- 总结
1. this:可以理解为当前对象 或当前正在创建的对象
*
* 2. this用来调用属性、方法;构造器
*
* 3.1 this在方法内调用属性、方法:
*
* ① 在类的方法中,如果方法的形参与类中定义的属性同名了,则为了区分方法内使用的变量是形参还是属性,我们需要
* 引入this关键字。
* > 使用this关键字调用的变量就是属性,不使用this关键字调用的变量就是形参。
* ② 在类的方法中,如果方法的形参与类中定义的属性不同名,或没有形参等情况,我们说在方法内调用属性的话,可以省略this.
* ③ 在类的方法中,可以调用其他的方法,此时的this关键字也可以省略。
*
* 3.2 this在构造器内调用属性、方法:
* ① 如果出现了构造器形参变量的名字与类中定义的属性同名了,则我们需要引入this关键字。
* > 使用this关键字调用的变量就是属性,不使用this关键字调用的变量就是形参。
* ② 如果出现了构造器形参变量的名字与类中定义的属性不同名或没有形参,我们说在构造器内调用属性的话,可以省略this.
* ③ 在类的构造器中,可以调用其他的方法,此时的this关键字也可以省略。
8.2 this调用构造器 - 略
9. 作业
- 练习
内存结构图:
-
练习
-
练习