目录
面向对象对象的三大特征:封装 继承 多态
抽象类 (abstract)
接口 (interface)
-
封装
封装:就是将显示世界事物的属性隐藏了(加入一个关键字private), 私有的,无法直接访问的,对外数据的安全性!(外界不能访问的)对外提供一个方法,公共的(public)访问方法(setXXX()/getXXX())
重新创建这个类 来优化上面的代码;加入封装思想
-
private关键字的特点?
- 被private修饰的成员变量以及成员方法只能在本类中访问
- 可以修饰成员变量也可以成员方法,就是为了外界不能直接访问
- 被private修饰的成员变量/成员方法可以间接的通过的方法访问;
-
局部变量和成员变量的区别?
1)书写位置不同:
成员变量:类中,方法外!
局部变量:方法定义中,或者方法声明上
2)内置中的位置不同
成员变量:堆内存中,和对象有关系
局部变量:栈内存中,和方法有关系
3)生命周期不同:
成员变量:随着对象的创建而存在, 随着对象的创建完毕等待jvm的垃圾回收器空闲 时候回收而消失
局部变量:随着方法的调用而存在, 随着方法调用结束而消失;
4)初始化值不同:
成员变量:是可以不初始化的,需要通过对象名来赋值,存在默认初始化;
局部变量:必须在使用之前初始化,否则报错;
class Demo{
//成员变量:类中方法外
// public int num = 20 ;
public int num ;
public void show(String a){ //局部变量
//局部变量
//int x = 10 ;
int x = 5 ; //必须使用之前初始化,否则下面x报错
System.out.println(a+x);// 报错信息Variable 'x' might not have been initialized
}
}
//测试类
public class VariableDemo {
public static void main(String[] args) {
//要去访问Demo类中num变量
Demo d = new Demo() ;
System.out.println(d.num); //对象名.成员变量名;
d.show("高圆圆");
}
}
- this :代表当前类对象的地址值引用
目的:就是为了区分 局部变量隐藏了成员变量:加入this可以区分
this.成员变量名 = 局部变量名 ;
-
匿名对象
格式:new 类名() ;
匿名对象调用方法
new 类名().成员方法名() ;
好处:节省内存空间,new 类名() ;
匿名对象可以作为参数传递
创建完毕之后,没有栈内存指向,所以使用完毕,立即被GC(垃圾回收器回收)
一般情况:匿名对象使用一次即可,这样不需要过多的去开辟堆内存空间,直接被回收;
class Student{
public void study(){
System.out.println("Good Good Study,Day Day Up!");
}
}
//定义StudentDemo类
class StudentDemo{
public void method(Student s){//形式参数是一个Student类型,引用类型
s.study();//调用该方法,应该使用实际参数对象名在访问
}
}
//测试类
public class NoNameDemo {
public static void main(String[] args) {
//要访问StudentDemo类中method方法
//创建StudentDemo类对象|
StudentDemo sd = new StudentDemo() ;
//创建学生对象
Student s = new Student() ;
sd.method(s); //方法里面:形式参数 创建学生了学生对象
System.out.println("---------------------------------");
//使用匿名对象new 类名()
//new 类名().成员方法名() ; //调用自己方法
sd.method(new Student());
System.out.println("---------------------------------");
//再次优化:
//链式编程(自己去玩,开发中很少)
new StudentDemo().method(new Student());
}
}
/**
* 一个类的成员方法:
* 类的成员变量:描述这个类有哪些属性
* 类的成员方法:setXXX()/getXXX():赋值和获取值的成员方法以及事物的一些其他行为
*
* 类的构造方法
* 什么是类的构造方法----->是一个方法,比较特殊
* 1)这个方法,方法名和类名相同
* 2)这个方法,连void都没有
* 3)构造方法是可以重载的;
* 无参构造方法
* 有参构造方法
*
*
* 构造方法的目的:就是为了类的成员相关的数据进行初始化的;
* 系统默认初始化
* 显示初始化;
*
* 注意事项:
* 当一个类中没有任何构造方法(无参/有参),系统会默认提供无参构造方法;
* 但是当如果提供有参构造方法,系统就不会在提供无参构造方法,无参构造就会报错;
*
* 以后,写一个类的时候,为了防止无参构造无法访问,永远出无参构造方法;
*
*
* 一个标准类的写法:
* 成员变量私有化
* 提供对外的公共访问方法,setXXX()/getXXX()
* 提供无参构造/ 有参构造方法 (根据需求进行判断)
*
*/
//定义一个学生类
class Student{
//给没有加入私有的name属性
String name ; //默认初始化
//无参构造方法
public Student() {
System.out.println("这个是Student类的无参构造方法...");
}
//有参构造方法
public Student(String name){//"高圆圆"
this.name = name ; //局部变量在给成员变量赋值;
System.out.println("这是Student类的有参构造方法"+name);
}
}
//测试类
public class ConstructorDemo {
public static void main(String[] args) {
//创建Student类对象
//类名 对象名 = new 类名() ;
Student s = new Student() ;//无参构造方法---->进行系统默认化
System.out.println(s);
System.out.println(s.name);
System.out.println("-----------------------------------") ;
//在创建一个新的学生对象
Student s2 = new Student("高圆圆") ;
System.out.println(s2);
System.out.println(s2.name);
}
}
-
static关键字的特点:
- 被静态修饰的随着类的加载而加载,优先于对象存在;
- 不能和this共存;
- 本身含义就是共享,共用;可以多个对象共享共用;
- 静态的使用场景适用于范围:(记忆的)
简单记忆:静态只能访问静态; 静态方法中只能访问静态变量静态方法只能调用静态方法;
非静态方法皆可以访问静态的东西也可以访问非静态的东西;
- ArrayTool: 对ArrayTool.java文件产生一个文档说明书---类似于API(Application Programming Interface:应用程序接口文档)
使用jdk安装目录里面的bin里面: javadoc.exe 指令 产生文档说书的指令
1)将ArrayTool这个java文件代码复制到本地某个磁盘上,类名和文件名称一致(先不要把package复制上去) 2)打开dos窗口,进入到这个java文件的路径下 (找到这个java文件后,这个类权限保证访问权限足够大:public)
3)会用javadoc -d 目录名称 -author -version ArrayTool.java(java源文件带后缀) 产生文档说明书
*ArrayTool类是针对数组操作的工具类,里面包含了数组的遍历功能,求数组最值问题,
* 获取数组的元素第一次出现索引值,冒泡排序等提供很多功能;
- 代码块: 使用{}括起来的内容
- 分类:
局部代码块,就是在方法定义中使用{}
{},作用:就是限定局部变量的生命周期;
构造代码块:---在类的成员位置定义的{},它有特点:在执行构造方法之前,如果类中有构造代码块,
优先执行构造代码块,作用:也可以给类的成员的数据进行初始化; (实际开发很少见到构造代码块)
静态代码块:
类就加载一次,静态代码块也就执行一次!
格式
static{ --->跟静态相关的都和类有关系,随着类的加载而加载;
里面书写代码
}
静态代码块,构造方法,构造代码块
优先级:静态代码块 > 构造代码块> 构造方法,而且每次执行构造方法之前,优先执行所有的构造构造代码块
-
继承
- 继承: 将多个类的共性内容抽取出来,放在一个独立的类中,让这个独立的类和其他类产生一种关系
"继承"---关键字 extends
格式: class 子类名 extends 父类名{}
- 继承的好处:
1)可以提高代码复用性
2)可以提高代码的维护性,后期便于维护,针对子类和父类进行维护(子父关系明确)
3)类与类产生的继承关系,是后面讲"多态"的前提条件;
在Java中有一个开发原则 "低耦合,高内聚"(以后所有的Java设计模式都需要遵循这一个原则)
耦合性:开发中是永远避免不了,可以降低(耦合:类和类的关系)
内聚性:指的是某个类完成某个功能的一种能力; 尽量不要产生继承关系来完成一个功能,一个类能完成一个类完成;
低耦合:降低耦合性,减少类和类的关系;
- 继承的特点:
1)在Java语言中,类和的类的关系是一种继承关系,这个继承只能支持"单继承",不支持多继承
2)类和类关系,虽然不支持多继承,但是层层单继承----> 多层继承
如果一个父类存在有参构造方法,没有无参构造方法,子类的所有构造会出现什么问题?出现了问题,怎么解决?
子类的所有全部构造方法报错,为什么? 子类继承父类,子类的所有构造方法都默认访问父类的无参构造方法
方案1:
1)手动给出无参构造方法
2)假设,人家现在就不需要让你给出父类的无参构造方法;
就需要让子类的构造方法,显示的访问父类的有参构造方法
----要使用关键字 super:代表父类空间标识(代表的父类对象的地址值引用!)
super() :访问父类的无参构造方法
super(xxx) :访问父类的有参构造方法
这些super一定是在子类构造方法中的第一句话
方案3) 保证子类的所有的构造方法某一个构造方法,让父类初始化完毕即可;
先通过子类的无参构造方法里面---this(xxx):访问本类(子类)有参构造方法
在子类的有参构造方法的第一话:让父类初始化 super(xxx):间接访问父类的有参构造方法
子类继承父类,一定要先执行父类的构造方法,初始化完毕之后;然后才能执行子类的构造方法---分层初始化
class Fu2{
/*public Fu2(){
System.out.println("这是Fu2的无参构造方法...");
}*/
public Fu2(String name){
System.out.println("这是Fu2的有参构造方法...") ;
}
}
//子类
class Zi2 extends Fu2{
//给出Zi2的无参构造方法
public Zi2(){
//super("随便给") ;
this("高圆圆") ; //访问本类的有参构造方法
System.out.println("这是Zi2的无参构造方法...");
//super("随便给") ; //一定是在第一句话
}
public Zi2(String name){
super("随便给") ;
System.out.println("这是Zi2的有参构造方法...");
}
}
//测试类
public class ExtendsDemo4 {
public static void main(String[] args) {
//创建子类对象
Zi2 z = new Zi2() ; //访问子类的无参构造方法
/* System.out.println("------------------------------") ;
Zi2 z2 = new Zi2("hello") ;*/
}
}
- 子类继承父类,成员变量的访问问题;
情况1: 子类和父类的中成员变量名称不一致,访问比较简单,分别访问即可!
情况2:子类和父类的成员变量名称一致: 如何访问呢?
1)先在子类的局部位置找,有没有这个变量,有就使用;
2)如果没有,在子类成员位置中找,有没有这个变量,有就使用;
3)如果子类的成员位置也没有,然后会在父类的成员位置找,有没有这个变量,有就使用;
4)如果父类的成员位置都没有,报错(前提:这个父类没有它的父类了),说明整个子父类中都没有变量;
遵循"就近原则" - 子类继承父类,关于成员方法的访问?
情况1:子类和父类的成员方法名称不一致,比较简单,分别调用即可;
情况2:子类和父类的成员方法一模一样:权限修饰符,返回值类型,参数列表都一样
子类将父类的方法覆盖了---->方法重写 :Override---->子类在父类的基础上,将父类的覆盖了,使用自己的功能;
举例: 动物都需要吃
猫类和狗类吃的东西不一样,将Animal类的eat进行重写;
- 什么是方法重写,什么是方法重载
方法重写是在子类存在方法与父类的方法的名字相同,而且参数的个数与类型一样,返回值也一样的方法
方法重载是一个类中定义了多个方法名相同,而他们的参数的数量不同或数量相同而类型和次序不同
方法重载的要求是参数列表不同。具体包括以下三种情形。
①参数的数量不同。
②参数的类型不同。
③参数的顺序不同。
- this和super的区别?
super()是从子类中调用父类的构造方法,this()在同一类调用其他构造方法
2.super和this均放在构造方法首行
3.this和super不能同时出现在一个构造函数里面,因为this必然会调用其它的构造函数,其它的构造函数必然也会有super语句的存在,所以在同一个构造函数里面有相同的语句,就失去了语句的意义,编译器也不会通过。
final:最终的,无法更改的,状态修饰符 ,被final修饰的成员方法,不能被重写,保证父类的方法安全性!
- final关键字特点:
- final可以修饰类,该类不能被继承
- final可以修饰变量,此时这个变量是一个"常量",常驻内存;
- final修饰成员方法,此时这个方法不能被子类重写,目的为了保证方法中某些数据的 安全性!
-
多态
多态:宏观角度:一个事物在不同时刻体现的不同形态
微观角度:具体对象在内存 中的变化
- 多态的前提条件:
1)必须有继承关系(类与类),没有继承关系,不谈多态
2)必须存在方法重写,子类部分功能要将父类的功能进行覆盖,重写,子类使用自己的功能体现
3)必须存在父类引用指向子类对象:
- 多态的成员访问特点:
父类名 对象名 = new 子类名();
1)成员变量:编译看左,运行看左;
2)成员方法:非静态 编译看左,运行看右,因为子类重写了父类的功能
静态方法:即使子类出现了和父类一模一样静态方法,不算重写,因为静态方法都算自己类相关的,类成员 编译看左,运行看左
3)构造方法:多态的前提条件,有继承关系,跟继承一样,分层初始化,先执行父类的构造方法,然后再是子类的构造方法
- 多态的弊端:
1)有继承关系了 2)有重写了 3)有父类引用指向子类对象 Fu f = new Zi() ; 无法调用子类的特有功能! 如何解决呢?
方案1:创建自己的子类对象
子类名 对象名 = new 子类名() ;
方案1不好地方:本身Fu fu = new Zi() ;已经开辟堆内存空间了,
Zi zi = new Zi();又要在堆内存开辟空间,消耗内存空间比较大 ;
方案2: 多态的第三个前提条件:父类引用指向子类对象
能不能将 将父类的引用 强制为子类的引用呢?
可以向下转型
前提必须向上转型Fu fu = new Zi() ;
Zi z = (Zi)fu ; //强转的语法 这样的好处:节省内存空间
- 多态的好处:
1)提高代码的复用性,由继承保证的
2)提高代码的扩展性,由多态保证 : 父类引用指向子类对象
- instaceof :关键字:
格式:一般是用于判断场景用的:
if(变量名 instanceof 引用数据类型(多态)){} 判断前面这个变量名是否为指定的数据类型(堆内存中的变化) true:表示是指定的类型;false:不是指定的类型
-
抽象类
- 什么是抽象类?
在一个类中,如果有抽象方法,这个类必须为抽象类 - 抽象类的特点:
1)不能实例化 (不能创建对象)
2)必须强制子类完成事情:必须将抽象方法重写!
抽象类的子类:
如果子类是一个抽象类, 没有意义(抽象类new不了) (前提条件是这个子类没有它的子类)
研究的抽象类的子类一定会有一个具体类,这个时候通过具体类才能创建对象;
抽象的父类名 对象名 = new 具体的子类名() ; 抽象类多态 -
abstract class Animal{ //必须为抽象类 //仅仅给出一个吃的声明,没有方法体的方法 ---->抽象方法 abstract public abstract void eat(); //没有方法体,必须要子类将方法进行"实现" //抽象方法 public abstract void sleep() ; //非抽象方法 public void show(){ System.out.println("show Animal"); } } //猫类 //abstract class Cat extends Animal{ //没有意义 class Cat extends Animal{ //具体类 @Override public void eat() {//只要见到具体的事物,才知道吃啥 System.out.println("猫吃鱼"); } //重写抽象类的sleep方法 @Override public void sleep() { System.out.println("猫躺着睡"); } } //狗类 class Dog extends Animal{ @Override public void eat() { System.out.println("狗吃骨头"); } @Override public void sleep() { System.out.println("狗趴着睡"); } } //测试类 public class AbstractDemo { public static void main(String[] args) { //创建父类对象 //Animal a = new Animal() ;//抽象类不能实例化(不能new) // 抽象的父类名 对象名 = new 具体的子类名() ; 抽象类多态 //Animal a = new Cat() ; //抽象类的子类是一个抽象类 Animal a = new Cat() ; //抽象类的子类是一个具体类 抽象类多态 a.eat(); //多态的成员方法:编译看左,运行看右! a.sleep(); a.show(); } }
- 抽象类的成员特点:
成员变量:抽象类的成员变量既可以有变量,也可以是自定义常量被final
成员方法:抽象类中既可以有抽象方法,也可也有非抽象方法
构造方法:既可以定义无参/有参构造方法...
存在抽象类多态,有继承关系,初始化的时候,构造方法----分层初始化---->先父类初始化,子类初始化 -
abstract class Person{ //抽象类 //定义一个成员变量 public int num = 100 ; //变量 public final int num2 = 200 ; //自定义常量 num2不能在改变 //人都要学习---具体的人才具备具体的动作行为----方法抽象化 public abstract void study(); //无参构造方法 public Person(){ System.out.println("这是Person类的无参构造方法"); } public void method(){//非抽象方法 System.out.println(num) ; System.out.println(num2); } } //学生类 class Student extends Person{ //无参构造方法 public Student(){ System.out.println("这是Student的无参构造方法"); } @Override public void study() { System.out.println("学生主要目的学习专业知识"); } } //测试类 public class AbstractDemo2 { public static void main(String[] args) { //抽象类多态测试 Person p = new Student() ; p.method(); p.study() ; } }
- abstract 和那些关键字冲突
和private关键字冲突:因为被private私有的成员方法只能在本类访问,而abstract修饰的成员方法 必须强制子类重写,已经超出来的当前类的范围
和final也冲突,被final修的成员方法,不能被重写;而抽象方法强制子类必须重写;
和static也冲突,abstract修饰的方法必须被子类重写,而static修饰的方法,算不上抽象,直接跟类相关的;
abstract关键字 应用范围: 定义在类上---抽象类
定义在方法上----抽象方法
public abstract 返回值类型 方法名(空参/带参...) ;
abstract 返回值类型 方法/名(空参带参...) ; - 子类继承父类,子类重写父类的抽象方法,必须保证访问权限足够大,要么加public要么跟父类的方法保持一致!否则报错;
-
接口
-
接口:体现这些事物本身不具备的功能,要经过一些特殊的实现才能具备功能-----称为 "接口"---关键字 interface
格式的写法
interface 接口名{ //命名规范和类名命名一样,见名知意 "大驼峰命名法"
只能为抽象方法
}
接口的子类----"子实现类"
实现
class 子类名 implements 接口名{
}
接口比抽象类还抽象---->特点:不能实例化
interface Jump{
/*public void jump(){ //接口中的只能是抽象方法
}*/
public abstract void jump() ;//因为 它默认修饰符 public abstract:可以省略不写
}
//跳高猫
class Cat implements Jump{
@Override
public void jump() {
System.out.println("猫可以跳高了");
}
}
//测试类
public class InterfaceDemo {
public static void main(String[] args) {
//Jump jump2 = new Jump() ;//接口不能实例化,需要通过子实现类实例化(必须为具体类)
//测试:接口多态---接口名 对象名 = new 子实现类名();
Jump jump = new Cat() ;
jump.jump();
}
}
接口---体现事物本身以外的额外功能,需要事物要对接口的功能要进行实现,才具备!
- 接口的定义
interface 接口名{} 接口和类名的命名规范同理,遵循"大驼峰命名法"
接口的最大特点: 不能实例化
如何实例化?
*接口通过它的子实现类进行实例化(前提,这个类实现 implements 接口)
class 子实现类名 implements 接口名{
}
实际开发中:接口的子实现类名的命名---> 接口名的后面+Impl:表示是接口的实现类
- 接口的成员特点:
1)成员变量:只能是常量,存在默认修饰符 :public static final
2)成员方法:只能是抽象方法,存在默认修饰符 public abstract
3)构造方法:没有构造方法的---通过子实现类的构造方法来实例化
接口本身意义:对外暴露这些功能,让子实现类实现 !
//定义接口
interface Inter{
// int num = 20 ; //隐藏了默认修饰符public static final 可以省略不写的
public static final int num = 20 ;
/*void show(){
System.out.println("show Inter");
}*/
//void show() ; //省略了默认修饰符 public abstract ,可以省略不写
public abstract void show() ;
/* public Inter(){ //不能这样写
}*/
}
//提供具体的子实现类
//实现
class InterImpl implements Inter{//实现类必须实现接口的抽象方法,否则报错
@Override
public void show() {
System.out.println("show InterImpl");
}
}
//测试类
public class InterfaceDemo {
public static void main(String[] args) {
//创建Inter对象
//Inter inter = new Inter() ; //接口不能实例化: Inter is abstract
//所以,通过接口指向子实现类名
//接口名 对象名 = new 子实现类名() ; //接口多态
Inter inter = new InterImpl() ;
System.out.println(inter.num); //编译看左,运行看左
//inter.num = 30 ; //报错:final修饰的变量:最终的.无法更改的, 不能在被赋值
System.out.println(Inter.num) ; //接口比抽象类还抽象 之前:类名.变量名; 变量被static修饰
//接口名.变量名 ; 变量被static修饰
}
}
关于面向对象中牵扯关系问题: Java中最基本的单元是类
类和类:继承关系 extends,Java语言中只支持单继承,不支持多继承,但是可以多层继承
类和接口: 实现关系 implements;一个类继承另一个类的同时,可以实现多个接口
接口和接口:继承关系:extends 不仅支持单继承,也可以多继承!
package com.qf.interface_01;
/*class GrandFu{}
class Fu extends GrandFu{}
class Zi extends Fu{}*/
//父接口
interface Love{
void love() ;
}
//父接口
interface StudyEnglish{
void speakEnglish() ;
}
//定义一个接口
interface Inter2 extends Love,StudyEnglish{ //接口与接口---> 继承关系 :单继承 / 可以多继承
void show() ;
}
interface Inter3{
void function() ;
}
//父类 ---抽象类
abstract class SuperIner{
public abstract void method();
}
//子实现类
class InterImpl2 extends SuperIner implements Inter2,Inter3{//一个类继承另一个类的同时,可以实现多个接口
//来自Inter2接口的方法
@Override
public void show() {
System.out.println("show InterImpl2");
}
//来自Inter3接口
@Override
public void function() {
System.out.println("function InterImpl2");
}
//来自抽象的父类
@Override
public void method() {
System.out.println("method InterImpl2");
}
//来自与Inteer2接口继承的另一个接口Love的接口的方法
@Override
public void love() {
System.out.println("爱生活,爱高圆圆...");
}
@Override
public void speakEnglish() {
System.out.println("会说英语");
}
}
//测试类
public class InterfaceDemo2 {
public static void main(String[] args) {
//谁的功能最多: 最具体的类 InterImpl2
//具体类 对象的创建
InterImpl2 interImpl2 = new InterImpl2() ;
interImpl2.function();
interImpl2.show();
interImpl2.love();
interImpl2.speakEnglish();
interImpl2.method(); //继承的它的抽象类的方法
}
}
- 抽象类和接口的区别?
抽象类:
构造方法:有构造方法,用于子类实例化使用(自己不能实例化)
成员变量:可以是变量,也可以是常量
成员方法:可以是抽象的,也就可以是非抽象的
接口:
构造方法:没有构造方法 不能实例化
成员变量:只能是常量
成员方法:只能是抽象的