Java学习笔记6.——面向对象
面向对象的三大特征:封装、继承、多态
文章目录
前言
一、面向对象介绍
什么是面向对象:
面就是拿,找,使用。
对象就是一个能干活的东西,面向对象就是拿一个能干活的东西。当然对象里面干的活不一样就需要拿不一样的对象干对应的活。
所以面向对象学习的是怎么使用一个已有的对象,当没有对象能干你想干的事情的时候你可以选择自己创建一个对象并设计,这就需要学习面向对象的语法了。
二、设计对象并使用
1.类和对象
类是对象的设计图,对象是一个具体实例
如果一个类写好了需要做什么事,但是没有创建成一个对象,那么这个类就是没有实现过,只是一个简单的类(设计图)
如果创建成了一个对象,这才代表一个具体的实例,有属性有方法,可以使用。
类中有什么
public class Person{
变量(属性)
方法(行为)
}
怎么创建对象
类名 对象名(可以自己取名字)= new 类名;
当类写完之后,需要创建一个Test类来使用这个类,此时就变成了对象是一个具体实例。
2.类的注意事项
代码如下(示例):
- 1.用来描述一类事务的类叫做javabean类,是不写main方法
- 2.编写mian方法的类,叫做测试类。在测试类中创建javabean类的对象并进行赋值调用。
- 3.类名首字母建议大写,需要见名知意,驼峰模式。
- 4.一个Java文件中可以定义多个class类,且只能一个类是public修饰,而且public修饰的类名必须成为代码文件名。为啥呢?为了防止命名冲突和其他问题(1.公共类的类名必须与文件名相同,如果一个类中有多个公共类,那么只有文件名和公共类名完全匹配的那个公共类会被编译,其他公共类将会报错。2.而且公共类可以被任何其他类访问和使用,这将会增大命名的难度。3.类是设计图,创建成对象后就是一个整体,不可能说你使用冰箱里面的一个制冷器,而不要其他的组合功能,这个类的结构就被破坏了不合理,除非你重新定义一个这样功能的类。)所以一个文件最后定义一个class类
- 成员变量完整格式:修饰符 数据类型 变量名 = 初始化值;一般无须指定初始化值,存在默认值。
对象成员变量的默认规则和以往一样,
数据类型 | 明细 | 默认值 |
---|---|---|
基本数据类型 | byte、short、int、long | 0 |
float、double | 0.0 | |
boolean | false | |
引用类型 | 类、接口、String、数组 | null |
属性的名字就是通过名词来寻找,方法就是通过行为来寻找。但是开发的时候程序员是有一个开发文档的,不需要自己去找了。
三、封装
封装是干什么的?
是告诉我们如何正确设计对象的属性和方法。
记住关键:
原则:对象代表什么,就封装对应的数据,给对应的数据提供对应的行为。
我看到视频里面有二个小案例,可以比对一下我是看懂了的。
1.人画圆,画圆是一个行为是封装在圆这个类中。因为人只是用笔(我理解为参数)调用了画圆这个方法。圆是自己画的。
2.人关门,关门是一个行为封装在门这个类中。因为关门的时候我们使用了推力然后门就关上了,门是自己关的。
3.人修门,修门是在门这个类中,人使用工具这个参数调用修门这个方法,所以修门是在门这个类中。不知道有没有大佬看到这个案例,对不对。
封装的好处就是让编程变得简单,有什么事就找对象,调用对象里面封装的方法(行为)就可以了。
不过你得了解一下有哪些对象干哪些事,当然可以百度了解,方便使用。
1.private关键字
- 是一个权限修饰符,是私有类,修饰成员属性和方法,只能在本类中使用,其他类就不能访问本来中私有方法或者属性。如果是一个私有类当然也不能被其他包中的类所访问,本包中的类可以访问。
- 当你不想这个类中的方法或者属性不被其他类访问就可以使用private修饰符,还能保证安全。但是当属性使用了private时就需要提供一个set和get一个赋值,一个返回值给别人。
2.this关键字
了解一下局部变量和成员变量。
当一个变量在方法外面,类里面时就是成员变量。
一个变量在方法里面时就是局部变量。
当有一个局部变量和成员变量是同一个变量名时,就区分不开了,所以当局部变量给成员变量赋值时,成员变量前面就要加上this.,否则你赋值之后就是null空的,没有赋值成功
this关键字在Java中的底层原理是通过指向当前对象的引用,让我们可以更方便地访问对象的属性和方法。
String name ;
int age;
String sex;
//赋值成功,结果就是你传进来的值
public void setName(String name){
this.name = name;
}
//赋值失败,输出结果是默认初始化的0
public void setAge(int age){
age = age;
String sex = "男";
System.out.println(sex):
}
就近原则
假如上面的代码输出sex的性别,它会优先输出离他最近的局部变量sex男,想使输出结果是你赋值的那就需要加上this.表示成员变量。
当然一个类中只有一个成员变量没有相同的成员变量,也就没有加上this的必要了。
我们可以思维扩展一下,if else语句中,当有多个if语句,然后只有一个else语句时,if是不是也有相同的就近原则。
那就是离else最近的那个if语句就是配套的,那遇到从上到下else在中间,上下都有if语句怎么办?
很简单,我们前面提到的流程控制语句中有三种控制语句,分别是顺序,分支,循环。顺序是默认的,这个时候从上到下里else最近的那个if就是配套的分支语句。
3.构造方法(构造器、构造函数)
构造方法是创建对象的时候,虚拟机自动的调用构造方法进行初始化,我觉得这个时候就可以不用创建set方法了。(作用)
构造方法的格式:
修饰符(public protest private) 类名(有参数,也可以没有参数){方法体;}
可以发现构造方法没有返回值类型,所以也没有返回值。
- 1.无参构造方法
public class Student {
String name;
int age;
String sex;
String schoolName;
public Student(){
}
}
但是方法体里面可以写其他的比如输出语句,但是不建议这样写,因为不规范可能导致使用这个方法时就出现别人不想要的效果。
因为公司里面的写项目不是一个人写,是一个组的人分配不同的任务写,别人可能会调用你写的类,这个时候大家就一定要在一个规范的规则下面进行编写,不然很难搞哦。
- 2.有参构造方法
public class Student {
String name;
int age;
String sex;
String schoolName;
public Student(){
}
public Student(String name,int age,String sex,String schoolName){
this.name = name;
this.age = age;
this.sex = sex;
this.schoolName = schoolName;
}
}
在每次创建对象的时候虚拟机就会调用你的构造方法,根据你有没有参数来调用无参构造方法还是有参构造方法。
所以不需要手动的调用。
当你使用有参构造方法时,你就需要在创建对象的时候就把值传过去。
public class StudentTest {
public static void main(String[] args) {
Student student = new Student("张三",18,"男","李四大学");
}
}
如果你不写参数,那就是调用的无参构造方法,需要你一个一个赋值。
public class StudentTest {
public static void main(String[] args) {
Student student = new Student();
student.name="张三";
......
}
}
关于构造方法注意:
如果我们没有写任何构造方法,系统是会构造一个无参构造方法
当我们写了任一个构造方法时,系统就不会构造了。比如你写了有参构造方法,系统不会给你构造无参构造方法了。
结合前面的方法重载,无参和有参构造方法也是属于重载,叫做构造方法的重载。
但是不论是否使用,建议两种构造方法都写,以免遇到其他情况可以灵活使用。
4.标准javabean类
结合上面一共有这几种。
- 1.方法名驼峰命名,见名之意,首字母大写
- 2.属性前面加上private修饰符,保证安全不随便赋值的
- 3.两个构造方法,有参和无参
- 4.成员方法
- 需要写上set和get方法
- 5.这个类本身的行为也是要写上的。
刚刚自己写了一个标准类就忘了写set和get方法了。idea也可以使用gpt插件生成标准javabean类。(设计图类的标准规范)
四、Java内存图
从JDK8开始,取消方法区,新增元空间,其原本区的功能被拆分放到了堆或者元空间。现在还是先称为方法区。
1.一个对象时
public class Student {
String name;
int age;
String sex;
String schoolName;
public Student(){
}
public Student(String name,int age,String sex,String schoolName){
this.name = name;
this.age = age;
this.sex = sex;
this.schoolName = schoolName;
}
public void study(){
System.out.println("学习");
}
}
//不是同一个类文件,这里只是方便观看
public class StudentTest {
public static void main(String[] args) {
Student student = new Student();
student.name="张三";
}
}
字节码文件(.class后缀名的文件)加载时进入内存方法区中,运行时main方法进入栈中,可以看到student测试类创建了student对象。
1.加载class文件,就是进入字节码文件进入内存方法区中(Test类先进入方法区,创建student对象的时候student类也进入方法区,有成员变量和方法的信息,是短暂存储,成员方法的地址是指向字节码文件中student.class处)
2.申明局部变量,就是student
3.在堆区申请一个空间,这个空间里面就是对象student,并且产生student地址,
4.默认初始化
5.显示初始化(因为类中一般这个类的属性都没有赋值,所以一般也没有)
6.构造方法初始化。因为我们创造力有参和无参构造方法,这里就不会构造了。
7.将地址值赋值给栈区的student
studentTest类中,如果直接输出student那么控制台显示的就是student在堆区中的地址。
如果直接输出没有赋值的属性那么输出的全是系统默认初始化值
如果赋值之后那么输出的属性就是赋的值
如果调用方法之后就输出学习,这时候study方法进栈,运行完了就出栈
这时候mian中的所有东西就运行完了,然后main就出栈,堆区这个对象也没有变量指向它,那它就成了个垃圾就不能在堆区中,就是堆区中的对象不被任何引用指向时就会被打上垃圾的标记,然后被垃圾回收器释放。
2.两个对象时
区别不大,就是堆区再创建一个对象,并且方法区中不需要再次引进student的字节码文件,堆区中的两个对象(地址不同)中的方法地址都是指向student字节码文件中的方法。
3.两个引用指向同一个对象
有时候会遇到下面这种把对象地址赋值给另一个,两个就指向同一个堆内存的同一个对象地址空间。
public class StudentTest {
public static void main(String[] args) {
Student student1 = new Student();
student1.name="张三";
Student student2 = student1;
student2.name="李四";
student1 = null;
System.out.println(student1.name+" "+student2.name);
}
}
这时候的输出结果是李四,因为student1的值被覆盖了
student1 = null;
System.out.println(student1.name);
System.out.println(student2.name);
null代表不存在的空间,这时候student1就不会指向堆内存的student空间地址,然后输出会报空指针异常。
但是此时student2的指向是没问题的,输出结果是李四。
如果student2也被赋值null那就也输出不了,报空指针异常。
引用数据就是引用其他空间的数据来使用
4.this关键字的内存图
this的本质:所在方法调用者的地址
public class Student {
String name;
public Student(){
}
public Student(String name) {
this.name = name;
}
public void study(){
String name = "李四";
this.name = name;
System.out.println(name);
}
}
//测试类,与上面的类不在同一个文件内
public class StudentTest {
public static void main(String[] args) {
Student student1 = new Student("张三");
student1.study();
}
}
以这个来举个例子。
如果有人看到这个测试类可以猜一下输出结果是什么?
输出结果是李四。
好了,回归问题。
this的作用是区分成员变量和局部变量。
this的本质是所在方法调用者的地址值。
student1就是调用了study()方法,student1就是调用者,this就是这个调用者的地址值(我猜就是student1把地址值赋值给this了,指向了堆区的对象)
所以study()方法中的虽然this.name= name,代表了name的值赋值给了堆空间中的name值,但是输出语句中没有this.name根据就近原则这里输出的就是参数的值,并不张三的值。
5.成员变量和局部变量
学了上面的知识更好的理解了这两种变量的区别
区别 | 成员变量 | 局部变量 |
---|---|---|
类中位置 | 方法体外,类中 | 方法体内 |
初始化值 | 默认初始化 | 手动初始化 |
存在位置 | 在堆区 | 在方法中 |
生命周期 | 随着对象的存在而存在,消失而消失 | 随着方法的调用而存在,运行结束而结束 |
作用域 | 整个类 | 整个方法 |
总结
我画了个思维导图总结,但是不知道为啥原图我粘贴不上来