hi,我是逸尘,一起学java吧
目标(任务驱动)
1.请熟练掌握面向对象的核心概念,三大特征。
场景:工作第五天,你的好友小张告诉你他在他们公司旁边的店里找到一个女朋友,这时你想想自己也不是没有对象呀,于是你打开了电脑,复习了JAVA面向对象的知识点。
2.请掌握包,内部类,抽象类,接口的知识点。
场景:你在复习JAVA面向对象的知识点时又回顾了这些内容。
对象是什么以及和类的关系
在早期开发我们结构的开发,规模越来越大,会出现很多问题,后来,我们结合对现实世界的理解然后建模,把单个物体对象,拿过来使用,也就是目标对象,进行一个开发。
比如日常生活中的草扫把,他就可以理解为一个对象,一个对象他有他自身的属性,比如,扫把有杆子作为支撑,还有下面用来扫地的高梁穗,还有他的颜色阿黄色,还有他的功能可以扫地。
对象有这些行为(功能)和属性的实例(确确实实存在的例子)。
那么如果说都有这样一个特性的泛指(都有这样的行为和属性的共通)那么我们可以统一称为类,比如说扫把类,那他的对象可可能还有塑料扫把,他们都有这样的特点,都有杆子,扫地功能,颜色属性,只不过具体内容不一样。
我们可以把类看出图纸,而对象成设计出来的实例。
也是说,对象是类抽象的实例
所以用这样的观点,万物皆对象。
你也是对象,华为手机也对象 ,中华田园犬也是对象。
类的创建和对象的创建
在前面的学习中我们知道类是什么,类的关键字是class,想要对象肯定先需要我们有一个类,在前面,我们多次创建类,但是没有具体的说明
在类中我们说都有这样那样的特性的泛指(都有这样的行为和属性的共通)
也就说类有他的属性,行为
这些对象的属性(类的属性)我们用成员变量(全局变量)来表示
这些对象的行为(类的行为)用成员方法来表示
同时我们类还有特殊的方法 构造方法(构造函数),可以为我们可以创建对象,相当于造图纸
在我们类还需要权限修饰符来修饰
同时类除了上面内容之外,还有一些高级特性,比如包和内部类,我们稍后会讲解。
我们先创建一个动物类
package com.ydrj.xb;
public class Animal {
//属性 成员变量
private int age;
private String name;
//行为 成员方法
public void call(){
System.out.println("叫");
}
//特殊的方法 构造方法(构造函数),只要当没有构造函数时才默认,当有
public Animal(){
}
//类的主方法,类的入口点
public static void main(String[] args) {
}
}
构造方法
和类同名,我们要创建的对象就是通过构造方法完成的。
需要注意的是
1.构造方法是没有返回值且不用void的
2.名称是和类名一样
3.默认可以不写,自动是无参构造,但是如果定义了非无参构造,那么无参构造还得从新自己写。
比如在一个类中
很像救济房一样,如果说没有房子,会给房,但是如果有别墅,就不给,但是需要救济房你可以额外的购买(在这里就是自己去定义)
有了这个构造函数我们就可以通过它来创建对象。
权限修饰符
另外我们还有权限修饰符主要有private,public和protected以及默认
修饰符 | 同类中 | 同一个包中(子类和无关类) | 不同包(子类) | 不同包(无关类) |
---|---|---|---|---|
public | Y | Y | Y | Y |
protected | Y | Y | Y | N |
默认 | Y | Y | N | N |
private | Y | N | N | N |
public都可以,private只要同类可以,protected保护在一个包,默认同包同类。
包
类除了成员变量,成员方法之外,还有一些高级特性,比如包和内部类。
包的作用是在大量的类中管理好文件,比如可以解决类重名问题。
Java API中提供了成千上万的类,肯定有重复的类名,这个时候我们解决办法就是在不同包下。
我们的包
也是我们开头的,我们的写法(idea会自动创建)
补充:
在我们的IDEA是这样调整文件的
import是导入确定的包,然后就可以使用对应的类了
当发生冲突时,可以用包名+类去解决,同包优先且可以省去包名,但是如果先导入安装导入来算
比如我们自己的包下有有一个Sacanner类和java.util包下的Sacanner类冲突
对象的创建
对象是一类事物抽取的特例
我们依托于我们的构造方法创建对象
类名 对象名=new 构造函数();
类名和构造函数的内容是一样的
如何访问对象的信息
因为对象是一个实例,要拿出对象信息用
对象名.成员方法
对象名.成员变量
比如我们创建一个动物类,需要创建一只哈士奇
package com.ydrj.xb;
//实际开发建议一个文件只定义一个class
public class Animal {
//属性 成员变量
private int age;
private String name;
//行为 成员方法
public void call(){
System.out.println(name+"在叫");
}
//特殊的方法 构造方法(构造函数),只要当没有构造函数时才默认,当有
public Animal(){
}
public Animal(String name,int age){
this.age=age;
this.name=name;
}
//类的主方法,类的入口点
public static void main(String[] args) {
//创建对象
Animal dog = new Animal("哈士奇",1);
System.out.println(dog.name);
//调用对象的方法
dog.call();
}
}
this关键字
this代表的是本类当前的对象
如果在构造方法中参数和成员变量相同名称,那么可以用this.成员变量相同名称代表对象的成员变量
简单的说在构造方法和成员方法代表当前对象地址
可以用于指定访问当前对象成员
对象的内存机制
方法区的方法去引用堆内存的引用地址的内容
栈内存存放的是对象的堆内存地址(对象名是存的引用地址)
堆内存是new出来的东西并且会开辟地址
垃圾回收
注意:当堆内存中的对象,没有被任何变量引用(指向)时,就会被判定为内存中的“垃圾”
用户无需担心废弃的对象占用内存,我们在java中也提供了一个finalize()的方法,可以清楚那些不new操作出来的的对象,这里我们不过多探究。
面向对象三大特征
封装,继承,多态
封装
将对象的行为和属性进行隐藏起细节,保证了类内部的完整性,使用户不能轻易的操作,只能执行公允的数据,避免了外部操作对内部的影响。
如何进行封装更好
●一般建议对成员变量使用private(私有、隐藏)关键字修饰进(private修饰的成员只能在当前类中访问) 为每个成员变量提供配套public修饰的的getter,setter方法暴露 其取值和赋值。
●不对外暴露的私有的方法
●单例模式
比如对成员变量使用private
正确
package com.ydrj.xb;
public class Test {
public static void main(String[] args) {
Student student = new Student();
student.setAge(12);
System.out.println(student.getAge());
}
}
package com.ydrj.xb;
public class Student {
public int getAge() {
return age;
}
public void setAge(int age) {
if (age>0&&age<150){
this.age = age;
}else {
System.out.println("年龄异常");
}
}
private int age;
}
JavaBean
那么我们也把分装数据的这个类叫JavaBean,也可以称为实体类。
继承
类与类之间有关联性,比如图形类和三角形类,这个就是继承的关联,继承是关联的一种,当然还有其他类与类的关联,这里就不过多赘述。
格式
修饰符 class 子类 extends 父类 {
}
使用继承的好处就是可以提高代码的复用性.
比如我们写程序员类的代码可以拿人类的属性和行为,我们只是在程序员类进行补充或者修改这样提高代码的复用性,提高开发效率,减少错误。
比如老板类和程序员类,他们都有很多相同的属性和方法,那么如果继承人类将会提高复用性
使用继承
父类
子类(减少很多重复代码)
内存分析
子类
父类
测试
注意点
Java只支持单继承,不支持多继承。也就是只有一个父亲,但是可以多层继承,子类A继承父类B,父类B可以继承父类C ,爷父孙关系。
子类不能继承父类构造方法,但是子类构造器默认先访问(调用)父类构造器,然后执行自己的构造器。
super
我们说子类构造器默认先访问(调用)父类构造器,然后执行自己的构造器。
那么他是怎么调用父类构造器的呢?
子类构造器的第一行语句默认都是: super() 不写也存在
就是子类有参构造器会默认调用父类无参构造器,用super才可以继承对应的父类有参构造器
我们最好写有参构造器的同时把无参构造器也写出来,如果不写(父类无参构造器),子类中用super(...),手动调用父类的有参构造器。
父类
子类
这样子类就可以于调用父类(超类)的构造函数。
除了调用父类(超类)的构造函数外
1.super还可以通过super.变量名就可以在子类中访问父类的成员变量
2.对于可继承的成员方法,如果子类在重写父类的方法同时想要调用(实现)与父类相同的方法,那么就用super.func(obj,obj,…)。
与构造函数不同的是,super.func()可以不用放在函数一开始的位置,而构造方法的super()必须放在函数体中最前面的位置。
方法重写
override
在继承体系中,子类出现了和父类中一模一样的方法声明,我们就称子类这个方法是重写的方法。
重写的是父类的方法内容
重写方法的名称、形参列表必须与被重写方法的名称和参数列表一致。
也有我们的快捷方式
package com.ydrj.xb;
//人类
public class People {
private int age;
private String name;
//肤色
private String skin;
//无参构造器 写有参构造器就要把他定义出来
public People(){
}
//有参构造器
public People(String skin){
this.skin=skin;
}
//行为方法
public void behavior(String name){
this.name=name;
System.out.println(name+"会劳动");
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
package com.ydrj.xb;
//老板类
public class Boss extends People{
//行为
public void getPaid(){
System.out.println("发工资");
}
//无参构造器
public Boss(){
//默认,不写也可以
super();
}
//有参构造器
public Boss(String skin){
//有参,要写
super(skin);
}
// //在子类中出现了和父类相同名称的方法我们就成为方法的重写
@Override
public void behavior(String name) {
System.out.println("创办公司");
super.behavior(name);
}
}
多态
同类型的对象,执行同一个行为,会表现出不同的行为特征。
首先我们要强调一下 多态存在的三个必要条件
- 继承或实现:在多态中必须存在有继承或实现关系的子类和父类
- 方法的重写:子类对父类中的某些方法进行重新定义(重写,使用@Override注解进行重写)
- 基类引用指向派生类对象,即父类引用指向子类对象:这里的父类类型是指子类对象继承的父类类型,或实现的父接口类型
格式
父类类型 对象名称= new 子类构造器;
接口 对象名称= new 实现类构造器;
这时我们的关系内容
我们主要看这个
使用多态的成员访问特点
成员方法
我们编译看左边,运行看右边
运行看右边
这种是向上转型,安全
可以换成new 子类() 便于解耦
解耦通俗解释就是好拆,像换电池
运行看右边我们已经很清楚了,就是调用子类重写父类的方法
编译看左边
成员变量
我们编译看左边,运行还是左边
因为我们的多态是侧重于行为的多态
多态下会产生的一个问题
在多态的情况下不能使用子类的独有功能
比如狗这个子类有独有的成员方法是play
独有的方法不能使用
因为在我们编译的时候,我们先编译我们父类,父类中没有这个方法直接报错,而我们的call,父类中有,运行的时候我们再去使用子类的方法,所以可以。
解决办法
首先要在编译时编译我们的子类,然后进行强制类型转换还是运行我们子类
父类类型对象转换为子类类型对象,这就是我们的向下类型转换
但是这种向下类型转换是不安全的,需要强制类型转换(父子之间要有继承关系)
但是又有一个问题
如果转型后的类型和对象真实类型不是同一种类型,那么在转换的时候就会出现ClassCastException
解决办法
instanceof
我们需要判断一下是不是我们的子类对象