第三周
回顾
private关键字的特点:
被private修饰的变量/方法只能在本类中访问,外界类不能直接访问
2)private修饰的成员变量以及成员方法不能直接访问,但是可以通过公共的访问间接访问.
3)private权限修饰符的范围最小,只能在本类中访问
封装(接WEEK02)
package com.qf.private_05;
/**
* 描述学生事物,
* 学生事物有两个属性,姓名和年龄,性别
* 定义成员show(),来展示学生信息
*
* 学生的行为: 学习JavaSE , 会敲代码
*
* 使用面向对象的方式,加入封装思想来测试学生事物
*
* 分析:
* 学生事物----- 定义学生类
* 1)分析属性
* 姓名, String name;
* 年龄, int age ;
* 性别 String sex ;
*
* 类中成员变量(属性),全部私有化加入private
* 2)分析事物行为:
* 对外提供一个方法,公共的(public)访问方法(setXXX()/getXXX())
*
* 成员show(),来展示学生信息 public void show(){输出name,age,sex}
* 学习JavaSE , 会敲代码 public void studyJavaSe()
* public void codding() 敲代码
*
*/
public class StudentTest {
public static void main(String[] args) {
//描述具体的学生---创建学生对象 :类名 对象名 = new 类名();
Student s = new Student() ;
//对象名.成员变量 = 赋值; //赋值这些值 肯定来数据库
//s.name = "高圆圆" ; //被private修饰的成员变量:只能在本类中访问,外界不能访问
// s.age = 20 ;
// s.sex = "女" ;
//使用对象名.成员方法名() ;
//给学生的信息赋值---调用setXXX()
s.setName("高圆圆") ;
s.setAge(43) ;
s.setSex("女") ;
System.out.println("学生的姓名是:"+s.getName()+",年龄是"+s.getAge()+",性别是:"+s.getSex());
//调用这个学生的一些其他行为
s.studyJavaSE();
s.codding();
}
}
基本类型作为形式参数和引用类型(数组,类,接口)作为形式参数的特点? (重点)
- 基本类型作为形式参数:形参的改变不会实际参数;
- 局部变量:生命周期:随着方法调用而存在,随着方法调用结束而结束;
*String作为形式参数(引用类型),和基本类型作为形式参数是一样,形式参数的改变不会影响实际参数; - 引用作为形式参数:形式参数的改变直接影响直接实际参数,除String类
面试题:局部变量和成员变量的区别?
-
1)书写位置不同: 成员变量:类中,方法外! 局部变量:方法定义中,或者方法声明上 2)内置中的位置不同 成员变量:堆内存中,和对象有关系 局部变量:栈内存中,和方法有关系 3)生命周期不同: 成员变量:随着对象的创建而存在, 随着对象的创建完毕等待jvm的垃圾回收器空闲 时候回收而消失 局部变量:随着方法的调用而存在, 随着方法调用结束而消失; 4)初始化值不同: 成员变量:是可以不初始化的,需要通过对象名来赋值,存在默认初始化; 局部变量:必须在使用之前初始化,否则报错; 什么时候把一个定义为成员变量的时机? 如果一个类能够描述这个事物的属性,必须为定义成员变量; 否则,都定义为局部变量;
: *1)方法的形式参数如果是基本类型,调用该方法的时候,实际参数需要传递对应数据值即可
- 2)方法的形式参数是引用类型:数组,类,接口,实际参数在进行传递需要传递当前数组对象 /类的对象/接口的对象
- new :创建实例(堆内存开辟空间:空间地址值)
this关键字
this用来定义本类中的变量
this:代表当前类对象的地址值引用 Student s = new Student() ; s---->空间地址值的引用;
this的出现:
为了防止局部变量隐藏了成员变量,
构造方法:
public Phne(String brand ,int price){
this.brand = brand ;
thi.price = price ;
}
setxxx()
public void setBrand(String brand){
this.brand = brand ;
}
this的用法:
this.变量名; 变量名:访问本类的成员变量名
this.方法名() ; 访问的本类的成员方法
this()----- 访问本类无参构造方法…
this(xxx) 访问本类的有参构造方法…
一个类成员的组成以及一个类的标准写法
一个类的组成
1)成员变量
2)构造方法
3)成员方法:
一个类的标准类的写法:
1)成员变量私有化
2)无参永远给出的,有参构造根据题意要求(没有明确,全部提供)
3)成员方法,根据题意要求(没有明确是否带参,是否有返回值,直接输出语句)
构造方法:
一个类的成员方法:
-
类的成员变量:描述这个类有哪些属性
-
类的成员方法:setXXX()/getXXX():赋值和获取值的成员方法以及事物的一些其他行为
-
类的构造方法
-
什么是类的构造方法----->是一个方法,比较特殊
-
1)这个方法,方法名和类名相同
2)这个方法,连void都没有
3)构造方法是可以重载的;
无参构造方法
有参构造方法 -
构造方法的目的:就是为了类的成员相关的数据进行初始化的;
系统默认初始化
显示初始化; -
注意事项:
当一个类中没有任何构造方法(无参/有参),系统会默认提供无参构造方法;
但是当如果提供有参构造方法,系统就不会在提供无参构造方法,无参构造就会报错;
以后,写一个类的时候,为了防止无参构造无法访问,永远出无参构造方法; -
一个标准类的写法: 成员变量私有化 提供对外的公共访问方法,setXXX()/getXXX() 提供无参构造/ 有参构造方法 (根据需求进行判断)
匿名对象
Java中有一个概念:匿名对象: 毋庸置疑,没有名字对象;
之前: 类名 对象名 = new 类名() ; 对象名
Student s = new Student() ; 里面的对象名是s
对象名.访问成员方法();
-
匿名对象: ***格式:new 类名() ;*** ***匿名对象调用方法 new 类名().成员方法名() ;***
-
好处: 节省内存空间,new 类名() ; 匿名对象可以作为参数传递 创建完毕之后,没有栈内存指向,所以使用完毕,立即被GC(垃圾回收器回收) 一般情况:匿名对象使用一次即可,这样不需要过多的去开辟堆内存空间,直接被回收;
package com.qf.noname;
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());
}
}
static关键字
static本身的含义:共享,共用,可以多个对象去共用;
被static修饰的成员变量或者成员方法---->随着类的加载而加载,优先于对象存在
被static访问方式的变量/方法—>静态变量/静态方法:
类名.变量名;
类名.方法名() ;
static使用场景:
一般就工具类(自定义的)或者jdk提供类:java.util.Arrays数组工具类
java.lang.Math:数学运算的工具类
java.util.Collections:针对集合工具类
他们的方法都是提供—static修饰的 (查看jdk的api文档)
一般说的成员的东西:都行需要对象来访问的
类名 对象名 = new 类名() ;
成员变量----非静态的
成员方法----非静态的
static关键字的特点:
-
1)被静态修饰的随着类的加载而加载,优先于对象存在; 2)不能和this共存; 3)本身含义就是共享,共用;可以多个对象共享共用; 这也是告诉我们,为什么将某个属性定义static 举例: 饮水机的水----共享,共用 自己水杯中的水---不行的 4)被静态修饰的成员变量或者成员方法(静态变量/静态方法) (掌握) 访问的方式: 可以使用对象名来访问但是,对于系统来说(jvm)静态的东西:变量/方法 都是类名直接访问; 类名.变量名; 类名.方法名()
-
上次用的java.lang.Math public static double random() Math.random() ;
-
静态的使用场景适用于范围:(记忆的) 简单记忆:静态只能访问静态; 静态方法中只能访问静态变量 静态方法只能调用静态方法; 非静态方法皆可以访问静态的东西也可以访问非静态的东西;
package com.qf.static_07;
public class PersonTest {
public static void main(String[] args) {
//测试
//描述 古代四大美女
//创建第一个人
描述古代四大美女,每一个人的国籍都是 "中国",从内存角度考虑,堆内存中开辟空间,为类的属性进行系统默认初始化,然后地址值产生,进行显示初始化,这个四个人的国籍都是一样的,比较耗费内存空间,这个程序看起来很臃肿(冗余度大)
解决问题:
* Java提供了一个关键字 static :本身含义:共有,共享的意思
* 在county上面加入一个关键字static ---->随着类的加载而加载(优先于对象的创建之前完成)
Person p1 = new Person("貂蝉",25,"中国") ;
p1.show();
//Person p2 = new Person("王昭君",18,"中国") ;
Person p2 = new Person("王昭君",18);
p2.show();
//Person p3 = new Person("西施",22,"中国") ;
Person p3 = new Person("西施",22);
p3.show();
//Person p4 = new Person("杨玉环",26,"中国") ;
Person p4 = new Person("杨玉环",26);
p4.show();
}
}
优化后如下:
package com.qf.static_07;
//人类
public class Person {
//属性:姓名,年龄和国籍,先不用私有
String name ; //姓名
int age ; //年龄
//String country ;// 国籍
static String country ;// 国籍
//永远给出无参构造方法
public Person(){}
//给出有参构造方法 ---带两个参数
public Person(String name,int age){
this.name = name ;
this.age = age ;
}
//带三个参的构造方法
public Person(String name,int age,String country){
this.name = name ;
this.age = age ;
this.country = country ;
}
//show方法,展示这个人具体信息
public void show(){
System.out.println("这个人姓名是:"+name+",年龄是:"+age+",所在的国籍是:"+country);
}
}
代码块
代码块: 使用{}括起来的内容
-
分类: 局部代码块,就是在方法定义中使用{} {},作用:就是限定局部变量的生命周期;
-
构造代码块:---在类的成员位置定义的{},它有特点:在执行构造方法之前,如果类中有构造代码块,优先执行构造代码块,作用:也可以给类的成员的数据进行初始化; (实际开发很少见到构造代码块)
*只要执行构造方法,如果存在构代码块,它必须优先执行,然后才是构造方法…
*
-
静态代码块:类就加载一次,静态代码块也就执行一次!
-
格式 static{ --->跟静态相关的都和类有关系,随着类的加载而加载; 里面书写代码 }
继承
将多个类的共性内容抽取出来,放在一个独立的类中,让这个独立的类和其他类产生一种关系
-
“继承”—关键字 extends
格式: -
class 父类名{ 共性内容:姓名,年龄..... 提供公共的访问方法setXXX()/getXXX() } class 子类名 extends 父类名{ }
-
继承的好处: 1)可以提高代码复用性 2)可以提高代码的维护性,后期便于维护,针对子类和父类进行维护(子父关系明确) 3)类与类产生的继承关系,是后面讲"多态"的前提条件;
在Java中有一个开发原则 “低耦合,高内聚”(以后所有的Java设计模式都需要遵循这一个原则)
*耦合性:开发中是永远避免不了,可以降低(耦合:类和类的关系)
*内聚性:指的是某个类完成某个功能的一种能力; 尽量不要产生继承关系来完成一个功能,一个类能完成一个类完成;
- 低耦合:降低耦合性,减少类和类的关系;
特点:
1)在Java语言中,类和的类的关系是一种继承关系,这个继承只能支持"单继承",不支持多继承 -
class 父{} class 父2{} class 子 extends 父,父2{} 多继承:不支持 class 子 extends 父{} :正常的语法格式
2)类和类关系,虽然不支持多继承,但是层层单继承----> 多层继承
关于的注意的问题:
-
1.子类继承父类,对于非私有的成员,直接可以继承过来,但是如果私有成员,它可以通过公共的访问可以访问,但是直接访问的; 2.被私有修饰的东西(成员变量/成员方法),只能在当前类访问的;
package com.qf.extends;
class Fu{
//非私有的成员变量
int num = 20 ;
//私有的成员变量
private int num2 = 50 ;
//非私有的成员方法
public void method(){
System.out.println("method Fu...");
System.out.println(num2); //间接通过公共的访问访问了num2
}
//私有的成员的方法
private void function(){
System.out.println("function Fu...");
}
//定义一个公共访问方法
public void function2(){
function(); //间接访问了私有方法
}
}
//子类
class Zi extends Fu{
//有一个自己的功能
public void show(){
System.out.println("show zi...");
}
}
//测试类
public class ExtendsDemo3 {
public static void main(String[] args) {
//创建子类对象
Zi zi = new Zi() ;
System.out.println(zi.num) ;
// System.out.println(zi.num2) ;//num2在父类中已经被private控制了
zi.method();
// zi.function() ; //private的
zi.function2();
}
在继承关系中,构造方法的访问问题(重点)
-
子类继承父类,子类的所有构造方法都默认访问父类的无参构造方法为什么?
子类继承父类,会用到父类的数据,需要让父类先进行初始化; 一个类初始化的—肯定需要执行构造方法的
- 面试题:
如果一个父类存在有参构造方法,没有无参构造方法,子类的所有构造会出现什么问题?出现了问题,怎么解决?
子类的所有全部构造方法报错,为什么?
子类继承父类,子类的所有构造方法都默认访问父类的无参构造方法
*方案1: -
1)手动给出无参构造方法 2)假设,不能给出父类的无参构造方法; 就需要让子类的构造方法,显示的访问父类的有参构造方法
要使用关键字super:代表父类空间标识(代表的父类对象的地址值引用!)
*
-
super() :访问父类的无参构造方法 super(xxx) :访问父类的有参构造方法 这些super一定是在子类构造方法中的第一句话
-
- 保证子类的所有的构造方法某一个构造方法,让父类初始化完毕即可;
先通过子类的无参构造方法里面
this(xxx)
访问本类(子类)有参构造方法
在子类的有参构造方法的第一话:让父类初始化 super(xxx):间接访问父类的有参构造方法
子类继承父类,一定要先执行父类的构造方法,初始化完毕之后;然后才能执行子类的构造方法—分层初始化
继承关系中:所有的子类的构造方法默认访问父类的无参构造方法;
- 保证子类的所有的构造方法某一个构造方法,让父类初始化完毕即可;
-
this和super的区别
-
this.变量名 :本类的成员变量
-
super.变量名: 父类的成员变量
-
this.方法名() ;本类的成员方法
-
super.方法名(); 父类的成员方法
-
this():访问本类的无参构造方法
-
super():访问父类的无参构造方法
-
this(XXX):访问的本类的有参构造方法
-
super(xxx):访问父类的有参构造方法
package com.qf.extends;
//父类
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进行重写;
面试题:
方法重写override和方法重载overload有什么区别?
方法重写:出现在继承中, 描述的子类继承父类的时候,可能沿用父亲的功能,而且使用
自己的功能,将父类的功能进行覆盖(复写/重写); 为了让子类具体体现出来的功能信息
final关键字
final关键字特点:
- 本身的含义:最终的,无法更改的
- 1)final可以修饰类,该类不能被继承
- 2)final可以修饰变量,此时这个变量是一个"常量",常驻内存;
-
//后期常用类:基本类型 int—> 自动提升 Integer 类自定义常量:在开发中: 定义一个int类型的 public static final 数据类型 xxx = 值; class Demo{ public static final int x = 100 ; // 自定义常量: 编译时期常量,不需要加载;jvm检查语法即可
public static final Integer i = new Integer(100) ; 运行时期常量,因为jvm需要加载Integer类
}
类名访问:
Demo.x
Demo.i
3)final修饰成员方法,此时这个方法不能被子类重写,目的为了保证方法中某些数据的 安全
package com.qf.final_08;
/**
* 类加载的时候,内存是很快的 继承关系
* 父类和子类跟静态相关的先执行
*
* 静态的东西优先于对象存在 (类名 对象名 = new 类名() ;)
*
* 静态代码块>构造代码块>构造方法....
*
* 继承关系:分层初始化---->先父类初始化----然后才是子类初始化
*/
class Fu2{
static{
System.out.println("Fu2的静态代码块");//1)
}
public Fu2(){
System.out.println("Fu2的无参构造方法");//4)
}
{
System.out.println("Fu2的构造代码块"); //3)
}
}
class Zi2 extends Fu2{
public Zi2(){
System.out.println("Zi2的无参构造方法");//6)
}
{
System.out.println("Zi2的构造代码块"); //5)
}
static{
System.out.println("Zi2的静态代码块"); //2)
}
}
//看程序,写结果
public class Test {
public static void main(String[] args) {
//创建子类对象
Zi2 z = new Zi2() ;
}
}
多态
什么是多态?
-
多态:
-
宏观角度(现实生活中):一个事物在不同时刻体现的不同形态 水事物: 气态,固态,液态 猫是动物 狗是动物 微观角度(内存中变化):具体对象在内存中的变化(对象在不同时刻的类型)
多态的前提条件 (重点)
-
1)必须有继承关系(类与类),没有继承关系,不谈多态! 2)必须存在方法重写,子类部分功能要将父类的功能进行覆盖,重写,子类使用自己的功能体现; 举例: 动物都需要吃; 猫和狗/猪/猴子 等等 具体动物,给出具体的吃的体现,所以应该将动物的吃进行重写; 3)必须存在父类引用指向 子类对象 :
固定格式
-
父 父 = new 子; class Fu{} class Zi extends Fu{} 父类名 对象名 = new 子类名() ; //向上转型:使用的父亲的东西 Fu fu = new Zi() ; //父类引用指向 子类对象 Animal a = new Cat() ;//Cat继承自Animal 猫是动物
多态的成员访问特点(很重要)
父类名 对象名 = new 子类名() ;
1)成员变量:
编译看左,运行看左 ;
2)成员方法 :
非静态方法:
编译看左,运行看右, 因为子类重写了父类的功能!
静态方法: 即使子类出现了和父类一模一样静态方法,不算重写,因为静态方法都是自己类相关的,类成员!
编译看左,运行看左;
3)构造方法:多态的前提条件,有继承关系,跟继承一样,分层初始化
先执行父类的构造方法,然后再是子类的构造方法
多态的好处:
1)提高代码的复用性,由继承保证的
2)提高代码的扩展性,由多态保证 : 父类引用指向子类对象
多态的弊端:
-
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 ; //强转的语法***
这样的好处:节省内存空间
针对多态的向下转型,前提必须有向上转型(父类引用指向子类对象)
package com.qf.oop_duota;
class Fu{
public void show(){
System.out.println("show Fu");
}
}
//子类
class Zi extends Fu {
public void show() {
System.out.println("show Zi");
}
//子类有自己的特有功能
public void playGame(){
System.out.println("会玩lol");
}
}
//测试类
public class DuoTaiDemo2 {
public static void main(String[] args) {
//父类引用指向子类对象
Fu fu = new Zi() ;
fu.show() ;//编译看左,运行看右
// fu.playGame() ; //编译看左,运行看右
//方案1
Zi zi = new Zi() ;//创建子类具体对象
zi.playGame();
System.out.println("--------------------------------") ;
//子类名 对象名 = (子类型)父类对象; //向下转型
//方案2
Zi z = (Zi) fu;//分号后面alt+enter ---回车 自动补全
z.playGame();
}
抽象
什么抽象类?
在一个类中,如果有抽象方法,这个类必须为抽象类
举例:
- 现实生活中,说到动物或者人—>这些事物都是概括性的,这些概括性的事物中都有自己的行为
- 动物都需要吃,但是只有见到具体的事物,才知道具体的动物吃什么类型食物,在代码定义动物类的时候,在吃个行为上不应该只给出具体体现(仅仅给出一个功能的声明),而是应该具体体现在具体的动物类中(动物类的子类);
- 给某个事物的一些行为,不给出具体体现,在Java编程中,应该加入一个关键字 abstract,这些行为—>“抽象方法”
- 有抽象方法的类一定是抽象类,
抽象的方法的格式:
根据写方法的格式一样,加入一个关键字abstract,而且没有方法体{}
public abstract 返回值类型 方法名(空参/带参) ;
抽象类的特点:
1)不能实例化 (不能创建对象)
2)必须强制子类完成事情:必须将抽象方法重写!
*抽象类的子类:
-
如果子类是一个抽象类, 没有意义(抽象类new不了) (前提条件是这个子类没有它的子类) 研究的抽象类的子类一定会有一个具体类,这个时候通过具体类才能创建对象; 抽象的父类名 对象名 = new 具体的子类名() ; 抽象类多态
抽象类的成员特点:
成员变量:抽象类的成员变量既可以有变量,也可以是自定义常量被final
成员方法:抽象类中既可以有抽象方法,也可也有非抽象方法
构造方法:
既可以定义无参/有参构造方法…
存在抽象类多态,有继承关系,初始化的时候,构造方法----分层初始化---->先父类初始化,子类初始化
面试题:
如果有一个类没有任何的抽象方法,还要将这个类定义为抽象类的意义?
意义:意义就是不能让它new,它如何实例化呢?肯定有具体的子类,进行抽象类多态来操作…
设计代码(设计层面)
package com.qf.abstract;
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() ;
}
}
面试题
问题1 : abstract 和那些关键字冲突
-
和private关键字冲突:因为被private私有的成员方法只能在本类访问,而abstract修饰的成员方法必须强制子类重写,已经超出来的当前类的范围 和final冲突,被final修的成员方法,不能被重写;而抽象方法强制子类必须重写; 和static也冲突,abstract修饰的方法必须被子类重写,而static修饰的方法,算不上抽象,直接跟类相关的;
*abstract关键字 应用范围: 定义在类上—抽象类
- 定义在方法上----抽象方法
public abstract 返回值类型 方法名(空参/带参…) ;
abstract 返回值类型 方法/名(空参带参…) ;
问题2:
子类继承父类,子类重写父类的抽象方法,必须保证访问权限足够大,要么加public要么跟父类的方法保持一致!
否则报错;
接口
什么是接口?
- 接口体现的是事物的一种额外功能 ;
- 设计理念: 体现的是一种 "like a"的关系
跳高猫
钻火圈狗
之前讲继承的概念—>设计理念体现的是一种"is a"的关系 :什么是什么的一种
水果
香蕉
苹果
橘子
接口—体现事物本身以外的额外功能,需要事物要对接口的功能要进行实现,才具备!
*接口的定义
interface 接口名{} 接口和类名的命名规范同理,遵循"大驼峰命名法"
接口的最大特点: 不能实例化
如何实例化?
接口通过它的子实现类进行实例化(前提,这个类实现 implements 接口) - class 子实现类名 implements 接口名{//实现
}
实际开发中:接口的子实现类名的命名—> 接口名的后面+Impl:表示是接口的实现类
接口的成员特点:
1)成员变量:只能是常量,存在默认修饰符 :public static final
2)成员方法:只能是抽象方法,存在默认修饰符 public abstract
3)构造方法:没有构造方法的—通过子实现类的构造方法来实例化
接口本身意义:对外暴露这些功能,让子实现类实现 !
Java编码中,体现这些事物本身不具备的功能,要经过一些特殊的实现才能具备功能-----称为 “接口”—关键字 interface
-
格式的写法 interface 接口名{ //命名规范和类名命名一样,见名知意 "大驼峰命名法" 只能为抽象方法 } 接口的子类----"子实现类" 实现 class 子类名 implements 接口名{ } 接口比抽象类还抽象---->特点:不能实例化
package com.qf.interface;
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();
}
}
接口和抽象类的区别(面试题)
1)成员的区别
成员变量:
抽象类:
可以是变量,也可以是自定义常量
接口: 成员变量只能是常量: 存在默认修饰符 public static final 修饰的 ,可以省略
成员方法
抽象类:
即可以是抽象方法,也可以是 非抽象方法
接口:
只能是抽象方法,存在默认修饰符:public abstract :可以省略
构造方法
抽象类:
既可以存在无参构造方法/有参构造方法,存在继承关系,分层初始化先父类初始化,再是子类初始化(子类需要用到父类中)
接口:没有构造方法的 ,意义:就是通过接口暴露给外面功能,实现这些功能接口
2)关系区别
Java中最基本的单元是类,Java本身就面向接口编程
类和类:继承关系 extends,Java语言中只支持单继承,不支持多继承,但是可以多层继承
类和接口: 实现关系 implements;一个类继承另一个类的同时,可以实现多个接口
接口和接口:继承关系:extends 不仅支持单继承,也可以多继承!
3)设计理念的区别
抽象类:
存在继承关系, 体现的是一种"is a"的关系 ,什么是什么的一种A类是B类的一种,B类是A类的一种 ,这个时候要使用继承;
接口:
子实现类 实现关系,体现的一种"like a"的关系,什么像什么的一种额外的扩展功能 ;