Java基础复习day06
Java基础复习day06
文章目录
- 面向对象(Object Oriented)
- 基本概念
- 设计对象并使用
- 类和对象
- 如何定义类
- 如何得到类的对象
- 如何使用对象
- 类的几个补充注意事项
- 面向对象的三大特征
- 封装(面向对象三大特征之一)
- 封装的基本概念
- 封装的注意事项
- 封装的好处
- private关键字
- this关键字
- 基本概念
- 格式
- 构造方法(也叫作构造器或构造函数)
- 构造方法的作用
- 构造方法的格式
- 构造方法的特点
- 构造方法的执行时机
- 构造方法注意事项!!
- 标准JavaBean
- 书写要求
- 对象在内存中的形式
- 一个对象在内存中的形式
- 补充:垃圾回收
- 两个对象在内存中的形式
- 两个引用指向一个对象
- 在循环中创建对象
- this的内存原理
- 基本数据类型和引用数据类型
- 基本数据类型
- 引用数据类型
- 成员变量与局部变量的区别
- 成员变量
- 局部变量
面向对象(Object Oriented)
基本概念
简单来说,面向对象就是找东西来解决对应的问题
设计对象并使用
类和对象
类: 是对象共同特征的描述
对象: 是真实存在的具体东西
在Java中,必须先设计类,才能获得对象
如何定义类
格式:
public class 类名{
- 成员变量(代表属性,一般是名词; 如: 姓名 年龄…)
- 成员方法(代表行为,一般是动词; 如: 吃饭 学习…)
- 构造方法(构造器)(给成员变量进行初始化)
- 代码块(之后补充)
- 内部类(之后补充)
}
// 手机类
public class Phone {// 属性String brand;double price;// 行为public void open(){System.out.println("开机....");}public void call(){System.out.println("打电话....");}}
如何得到类的对象
类名 对象名 = new 类名();
Phone p = new Phone();
如何使用对象
访问属性: 对象名.成员变量
访问行为: 对象名.方法名(…)
//测试类
public class Test {public static void main(String[] args) {// 创建手机对象Phone p1 = new Phone();// 给对象赋值p1.brand = "huawei";p1.price = 2888;// 获取对象的值System.out.println(p1.brand);System.out.println(p1.price);// 调用对象中的方法p1.open();p1.call();// 创建新对象Phone p2 = new Phone();// 给对象赋值p2.brand = "小米";p2.price = 2000;// 获取对象的值System.out.println(p2.brand);System.out.println(p2.price);// 调用对象中的方法p2.open();p2.call();}
}
类的几个补充注意事项
-
用来描述一类事物的类,专业叫做JavaBean类;在JavaBean类中,是不写main方法的
-
类名首字母建议大写,满足“驼峰模式”,不能用关键字,必须是合法的标识符,且有意义
-
一个Java文件中可定义多个class类,但只能一个类是public修饰,而且public修饰的类名必须成为代码文件名;在实际开发中建议还是一个文件定义一个class类
-
成员变量的完整定义格式为:修饰符 数据类型 变量名称 = 初始化值; 一般无需指定初始化值,存在默认值
-
默认值规则:
byte short int long 0
double float 0.0
boolean false
引用类型: 类、接口、数组、String null
面向对象的三大特征
[封装](# 封装(面向对象三大特征之一)): 即正确的设计对象的属性和方法
继承: 之后补充
多态: 之后补充
封装(面向对象三大特征之一)
封装的基本概念
即正确的设计对象的属性和方法
封装的注意事项
对象代表什么,就得封装对应的数据,并提供数据对应的行为
封装的好处
- 让编程变得简单,遇见问题 找对象 调用方法就可以了
- 降低学习成本
private关键字
- private是一个权限修饰符
- 可以修饰成员(成员变量和成员方法)
- 被private修饰的成员只能在本类中才能访问
- 针对于每一个private修饰的成员变量,如果需要被其他类使用,需提供对应的get和set方法来使其他类对本类成员变量进行操作
- setXx(形参表)方法: 方法用public修饰,一般无返回值,用于其他类给本类成员变量赋值
- getXx()方法: 方法用public修饰,有返回值,用于其他类获取本类成员变量的值
// Phone类
public class Phone {// 属性private String brand;private double price;// 针对于每一个私有化的成员变量,都要提供get和set方法// 对于brand的get和set方法// getBrand:对外提供成员变量brand的值// setBread:给成员变量brand赋值public String getBrand(){return brand;}public void setBrand(String b){brand = b;}// 对于price的get和set方法// getPrice:对外提供成员变量price的值// setPrice:给成员变量price赋值public double getPrice() {return price;}public void setPrice(double p) {price = p;}// 行为public void open(){System.out.println(brand + "开机....");}public void call(){System.out.println(brand + "打电话....");}}
//测试类
public class Test {public static void main(String[] args) {// 创建手机对象Phone p1 = new Phone();// 给对象赋值p1.setBrand("huawei");p1.setPrice(2888);// 获取对象的值System.out.println(p1.getBrand());System.out.println(p1.getPrice());// 调用对象中的方法p1.open();p1.call();System.out,println("=============");// 创建新对象Phone p2 = new Phone();// 给对象赋值p2.setBrand("小米");p2.setPrice(2000);// 获取对象的值System.out.println(p2.getBrand());System.out.println(p2.getPrice());// 调用对象中的方法p2.open();p2.call();}
}
this关键字
基本概念
this的作用: 区分局部变量的成员变量
this关键字代表当前对象的地址,可以用于指定访问当前对象的成员变量、成员方法
格式
this.变量名
this.方法名()
Java寻找变量遵循"就近原则",如果程序中存在同名的变量,则会寻找距离最近的变量
例:
public class Find{private int age;// 成员变量public void method(){int age = 10;// 局部变量System.out.println(age);// 10 获取距离最近的age值
// System.out.println(this.age);// 0 获取当前对象的age值 即成员变量}
}
public class Test{public static void main(String[] args){Find f = new Find();f.method();}
}
上面的Phone类还是存在一个问题的:
Phone类中的set方法的形参b和p无法直观地看出是什么意思,影响了代码的可读性,但如果改成brand和price后程序则会根据就近原则赋值.从而使传入的brand/price值赋值给了形参自己,而并未赋值给成员变量,从而出现错误,这时我们就应该使用this关键字,使其指明哪一个是当前类中的成员变量
故上面的Phone类中的set方法可改进为:
// Phone类
public class Phone {// 属性private String brand;private double price;// 针对于每一个私有化的成员变量,都要提供get和set方法// 对于brand的get和set方法// getBrand:对外提供成员变量brand的值// setBread:给成员变量brand赋值public String getBrand(){return brand;}public void setBrand(String brand){this.brand = brand;}// 对于price的get和set方法// getPrice:对外提供成员变量price的值// setPrice:给成员变量price赋值public double getPrice() {return price;}public void setPrice(double price) {this.price = price;}// 行为public void open(){System.out.println(brand + "开机....");}public void call(){System.out.println(brand + "打电话....");}}
构造方法(也叫作构造器或构造函数)
构造方法的作用
构造方法在创建对象时,会被虚拟机自动调用,其作用是给成员变量进行初始化
构造方法的格式
构造方法分为有参(带参)构造方法和无参(空参)构造方法
有参构造方法 可以在初始化对象的同时给对象的成员变量赋值;代码格式为:
修饰符 类名(形参表){
方法体;(一般为赋值操作,除特殊需求)
}
无参构造方法 初始化对象时,对象的成员变量均采用默认值;代码格式为:
修饰符 类名(){
方法体;(一般没有内容,除特殊需求)
}
例:
public class Student {private String name ;private int age ;// 无参构造方法public Student(){}// 有参构造方法public Student(String name, int age){this.name = name;this.age = age;}
}
构造方法的特点
- 方法名与类名相同,大小写也要一致
- 没有返回值类型,连void都没有
- 没有具体的返回值(不能由return带回结果数据)
构造方法的执行时机
- 构造方法在创建对象的时候由虚拟机自动调用,不能被手动调用
- 每创建一次对象,就会调用一次构造方法
构造方法注意事项!!
- 如果自己没有写任何构造方法,Java会默认提供给我们一个无参构造方法
- 如果自己定义了构造方法,Java将不会提供默认的无参构造方法(即如果我们自己只写了一个有参构造方法,Java同样不会提供默认的无参构造方法,那么此时就一定要手写一个无参构造方法,否则在调用无参构造方法时会出现错误)
- 构造方法可以重载;有参构造方法和无参构造方法,两者的方法名相同,但参数不同,这叫做构造方法的重载
- 推荐规范: 无论是否使用,我们都需要手写一个无参构造方法和一个带全部参数的有参构造方法
// Student类
public class Student {private String name ;private int age ;// 无参构造方法public Student(){System.out.println("无参构造方法被调用了");}// 有参构造方法public Student(String name, int age){System.out.println("有参构造方法被调用了");this.name = name;this.age = age;}public String getName() {return name;}public void setName(String name) {this.name = name;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}
}
// 测试类
public class TestOfStudent {public static void main(String[] args) {// 创建对象// 调用无参构造方法Student s1 = new Student();// 无参构造方法被调用了System.out.println(s1.getName());// nullSystem.out.println(s1.getAge());// 0System.out.println("====================");// 调用有参构造方法Student s2 = new Student("kunkun",20);// 有参构造方法被调用了System.out.println(s2.getName());// kunkunSystem.out.println(s2.getAge());// 20}
}
标准JavaBean
书写要求
- 类名要见名知意
- 成员变量要用private修饰
- 提供至少两个构造方法
- 无参构造方法
- 带全部参数的有参构造方法
- 提供每一个成员变量对应的getXx()和seXxt()方法
- 如果还有其他行为(如学生类的"早读" “上课”…),也需要写上
对象在内存中的形式
拓展:从JDK8开始,取消了方法区,新增元空间,把原来方法区的多种功能进行拆分,有的功能放到了堆中,有的功能放到了元空间中
当运行一个类时,这个类的字节码文件就会加载到元空间中临时存储.
一个对象在内存中的形式
创建一个对象:
Student s = new Student();
以下是在内存中的具体步骤:
- 加载class文件
- 申明局部变量
- 在堆内存中开辟一个空间
- 默认初始化
- 显示初始化
- 构造方法初始化
- 将堆内存中的地址值赋值给左边的局部变量
例:
public class Student {String name = "张三";int age;public Student(){// 默认自带的无参构造方法,写不写都有,创建对象时会被虚拟机自动调用}public void study(){System.out.println("学习....");}
}
// 理解对象创建 在内存中的步骤
// 1.将TestOfStudent.class的字节码文件加载到元空间中临时存储
public class TestOfStudent {// 2.main方法作为程序的主入口,进入栈内存(入栈)开始运行public static void main(String[] args) {// 1 2// 3.将Student.clss的字节码文件加载到元空间中,并获取其所有的成员(成员变量 成员方法)// 4.在main方法中开辟一个空间s,用于存储Student类型变量的地址值// 5.在堆内存中开辟一个空间,并将Student类中的所有成员变量拷贝到该空间中,并且将Student类中的// 所有成员方法的地址也放在此空间中// 6.进行默认初始化:将默认值赋值给各个成员变量// 7.进行显示初始化:如果在定义成员变量的时候赋了值,如Student类中的第二行操作,那么这个"张三"会在// 这时候被显示初始化给成员变量.此时默认值null就会被"张三"所覆盖.(如果在定义变量时没有赋值,那么这// 个步骤可以忽略,如Student类中第三行的age就不用进行显示初始化,依然为默认值0)// 8.进行构造方法初始化:如果new了有参构造方法且传入了值,此时对应的成员变量的值将会被传入的值所覆盖//(如果new的是无参构造方法,那么这一步骤也可以忽略)// 9.将堆内存中空间的地址值赋值给空间s(局部变量)Student s = new Student();// 3 4 5 6 7 8 9// 打印堆内存空间的地址值System.out.println(s);// com.puddingww.ooDay02.Student@3b07d329// 打印堆内存空间中的 name和age 的值System.out.println(s.name + "....." + s.age);// 张三.....0s.name = "kunkun";// 将kunkun赋值给堆内存空间中的names.age = 20;// 将20赋值给堆内存空间中的age// 打印堆内存空间中的 name和age 的值System.out.println(s.name + "....." + s.age);// kunkun.....20// 在堆内存空间中通过成员方法的地址在元空间中找到study()方法,study()方法进栈开始运行s.study();// 学习....// study()方法运行完毕 出栈}// main()方法运行完毕 出栈 空间s消失// 此时没有变量指向堆内存中的空间了,该空间将被当做垃圾清理
}
补充:垃圾回收
Java存在自动垃圾回收器,会定期进行清理,以提高性能
如果==一个对象未被任何变量引用(指向)==时,就会被判定为内存中的“垃圾”。会被Java的垃圾回收器从内存中清理
两个对象在内存中的形式
在创建第二个对象时,不需要再加载一遍类的字节码文件了
注意: 创建两个对象会开辟两个空间,两个空间中赋值等操作互不影响
例:
public class Student {String name = "张三";int age;public Student(){// 默认自带的无参构造方法,写不写都有,创建对象时会被虚拟机自动调用}public void study(){System.out.println("学习....");}
}
// 理解对象创建在内存中的步骤
// 1.将TestOfStudent.class的字节码文件加载到元空间中临时存储
public class TestOfStudent {// 2.main方法作为程序的主入口,进入栈内存(入栈)开始运行public static void main(String[] args) {// 1 2// 创建对象 在内存中的步骤 3~9Student s = new Student();// 假设地址为001// 打印堆内存空间的地址值System.out.println(s);// 001// 打印堆内存空间001中的 name和age 的值System.out.println(s.name + "....." + s.age);// 张三.....0s.name = "kunkun";// 将kunkun赋值给堆内存空间001中的names.age = 20;// 将20赋值给堆内存空间001中的age// 打印堆内存空间001中的 name和age 的值System.out.println(s.name + "....." + s.age);// kunkun.....20// 在堆内存空间001中通过成员方法的地址在元空间中找到study()方法,study()方法进栈开始运行s.study();// 学习....// study()方法运行结束,出栈System.out.println("========================");// 注意:!!不需要再次加载Student.class的字节码文件到元空间中了,故步骤3省略// 重复步骤 4~9Student s1 = new Student();// 假设地址为002// 打印堆内存空间的地址值System.out.println(s1);// 002s1.name = "只因";// 将"只因"赋值给堆内存空间002中的names1.age = 18;// 将18赋值给堆内存空间002中的age// 打印堆内存空间002中的 name和age 的值System.out.println(s1.name + "....." + s1.age);// 在堆内存空间002中通过成员方法的地址在元空间中找到study()方法,study()方法进栈开始运行s1.study();// study()方法运行完毕 出栈}// main()方法运行完毕 出栈 空间s s1消失// 此时没有变量指向堆内存中地址为 001 002 的空间了,这些空间将被当做垃圾清理
}
个人总结: 创建一个对象就是在堆内存中开辟一个空间,并在该空间中拷贝了其类的成员变量和成员方法的地址值,可通过成员方法的地址值来调用成员方法
两个引用指向一个对象
// Student类
public class Student {String name;char sex;String hobby;public void study(){System.out.println("姓名:" + name + ",性别:" + sex + ",爱好:" + hobby+ ",开始学习了!");}
}
// 目标:理解两个变量指向同一个对象的内存运行机制// 将Test.class的字节码文件加载到元空间中临时存储
public class Test {public static void main(String[] args) {// 创建对象 堆内存开辟空间 并将地址赋值给变量s1Student s1 = new Student();s1.name = "小明";s1.sex = '男';s1.hobby = "睡觉、学习、打游戏";s1.study();// 姓名:小明,性别:男,爱好:睡觉、学习、打游戏,开始学习了!System.out.println("==================================");Student s2 = s1;// 将s1中存储的对象的地址赋值给了s2s2.name = "小红";// 根据地址修改成员变量的值s2.hobby = "提问题";// 根据地址修改成员变量的值System.out.println(s1.name);// 小红System.out.println(s2.name);// 小红System.out.println(s2.sex);// 男System.out.println(s1.hobby);// 提问题s2.study();// 姓名:小红,性别:男,爱好:提问题,开始学习了!s1 = null;// 即断掉了s1与堆内存空间的联系System.out.println(s1.name);// s1无法找到堆内存开辟的空间 出现NullPointerException 空指针异常System.out.println(s2.name);// 小红s2 = null;//注意: 此时s1与s2的赋值从 对象Student()的地址 变为 空,即对象Student()的地址未被任何变量引用(指向),则其将会被Java的垃圾回收器从堆内存中清理出去}// main方法运行完毕,出栈
}
在循环中创建对象
当我们在循环中创建对象时,每次循环都会创建(指向)一个新的对象(对象地址),并在循环结束后进入下一次循环时,再次指向新的对象地址,上次循环产生的对象(对象地址)因为没有被任何变量引用(指向)了,故被当做垃圾回收.
在循环中创建对象通常适用于要批量创建对象并存入集合或数组的操作;
该操作每次循环会创建新的对象,并将对象地址存入数组或集合,当进行下次循环时,创建新的对象,故指向新对象的地址;上次循环创建的对象地址因为存入了数组或集合,故有索引指向其地址,不会被当做垃圾清理
比如下面的例子:
// 车类
public class Car {// 成员变量private String name;// 品牌private double price;// 价格private String color;// 颜色// 构造方法public Car(){}public Car(String name , double price , String color){this.name = name;this.price = price;this.color = color;}// get set 方法//略
}
import java.util.Scanner;/*** 需求:* 定义数组存储三部汽车对象* 汽车的属性:品牌,价格,颜色* 创建三个汽车对象,数据通过键盘来录入,并把数据存入到数组中*/
public class TestOfCar {public static void main(String[] args) {// 将创建汽车数组Car[] cs = new Car[3];// 调用录入方法setCars(cs);System.out.println("信息录入完毕~");// 调用输出方法getCars(cs);}// 录入汽车数据的方法public static void setCars(Car[] cs){// Car c = new Car(); 错误的!!!!!!!// 创建扫描器,获取键盘录入Scanner sc = new Scanner(System.in);//循环创建对象录入信息存入数组for (int i = 0; i < cs.length; i++) {// 创建汽车对象Car c = new Car();// 开始录入信息System.out.println("请输入第" + (i+1) + "辆车的品牌:");c.setName(sc.next());System.out.println("请输入第" + (i+1) + "辆车的价格:");c.setPrice(sc.nextDouble());System.out.println("请输入第" + (i+1) + "辆车的颜色:");c.setColor(sc.next());// 将对象存入数组cs[i] = c;}}// 遍历输出汽车信息的方法public static void getCars(Car[] cs){for (int i = 0; i < cs.length; i++) {System.out.println("第" + (i+1) +"辆车: 品牌:" + cs[i].getName()+" 价格:" + cs[i].getPrice()+" 颜色:" + cs[i].getColor());}}
}
上面的例子中,将创建对象的语句放在了循环中,这样每次循环都会创建一个新的对象让我们录入信息并存入数组,实现了批量创建对象存入集合或数组的功能
注意点补充: 如果该创建对象的语句放在了for外面,则每次循环指向的都是同一个对象的地址,即数组中的每一个索引存入的都是相同的地址,那么我们无论进行了多少次循环,最后输出数组中全部对象的信息时会发现,数组中所有对象的信息都是我们最后录入的那一次的信息;这是由于数组中的地址都指向了同一个对象,而我们每次录入都只相当于修改这个对象的信息,故所有索引指向的对象信息会变成我们最后一次录入的信息
this的内存原理
this的本质: 表示所在方法调用者的地址
// Student 类
public class Student {private int age ;public void method(){int age = 10;System.out.println(age);// 就近原则,故该语句获取的是局部变量age的值,即10System.out.println(this.age);// this代表所在方法调用者的地址,即对象s的地址值,即为对象的成员变量age,即0}
}
// 测试类
// 1.将Test.class的字节码文件加载到元空间中临时存储
public class Test {// 2.main方法入栈public static void main(String[] args) {// 步骤3~9Student s = new Student();// 变量s调用成员方法,方法进栈s.method();// 方法出栈}// main()方法运行完毕 出栈 空间s消失// 此时没有变量指向堆内存中的空间了,该空间将被当做垃圾清理
}
我们再来看另一个例子:
// Student类
public class Student {private int age ;public int getAge() {return age;// 就近原则,即成员变量age的值}// 值传入,被形参age接收public void setAge(int age) {this.age = age;// 将形参接收的age值赋值给调用者s地址值指向的空间中的age}
}
// 测试类
// 1.将Test.class的字节码文件加载到元空间中临时存储
public class Test {// 2.main方法入栈public static void main(String[] args) {// 步骤3~9Student s = new Student();// 变量s调用set方法,方法进栈,传入实参s.setAge(18);// set方法出栈// 变量调用get方法,方法进栈,获取age的值s.getAge();// get方法出栈}// main()方法运行完毕 出栈 空间s消失// 此时没有变量指向堆内存中的空间了,该空间将被当做垃圾清理
}
基本数据类型和引用数据类型
基本数据类型
数据值是存储在自己的空间中的
特点:在赋值给其他变量时,也是赋的真实的值
基本数据类型变量在被定义的时候,会在栈内存中开辟了一个空间,里面存储的是真实的值
引用数据类型
数据值是存储在其他空间中,自己空间中存储的是地址值
特点:赋值给其他变量时,赋的是地址值
当定义引用数据类型的变量时,会在堆内存中开辟一块空间来存储数据的值,并将该空间的地址赋值给变量名,直接输出变量名会在控制台打印引用类型数据所在的地址值
成员变量与局部变量的区别
成员变量
- 类中方法外的变量
- 有默认的初始化值
- 变量在堆内存中的对象中(创建对象时在堆中开辟空间,同时将元空间的类中的成员变量拷贝到了该空间中)
- 随着对象的创建而存在,随着对象的消失而消失
- 作用域为整个类
局部变量
- 方法中、方法申明上(即形参) 的变量
- 没有初始化值,在使用之前需要手动完成赋值
- 变量在栈内存中的方法中
- 随着方法的调用而存在,随着方法的运行结束而消失
- 作用域为其所属的方法
本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!
