【Java之旅】 11. 抽象类与接口
11.1 final关键字
在Java中,final关键字可以用于修饰类、方法和变量
a. 修饰类
当我们用final关键字去修饰一个类时,那么这个类就会断子绝孙。他将不能被其他的类继承.
public class Finaltest
{
public static void main(String[] args)
{
Players kupurk = new Players();
System.out.println(kupurk.id);
}
}
final class Players
{
final int id = 1;
//this.id = 1;
}
class Attackers extends Players
{
}
报错
Finaltest.java:16: 错误: 无法从最终Players进行继承
class Attackers extends Players
^
1 个错误
b. 修饰方法
“ 使用final方法的原因有两个。第一个原因是把方法锁定,以防任何继承类修改它的含义;第二个原因是效率。在早期的Java实现版本中,会将final方法转为内嵌调用。但是如果方法过于庞大,可能看不到内嵌调用带来的任何性能提升。在最近的Java版本中,不需要使用final方法进行这些优化了。”-----《Java编程思想》
final意为 “最终的”,与final修饰后的类一样,在父类中被final修饰过的方法无法被子类覆盖,所以子类不可,无法也就是说被final修饰后的方法方法不能被子类重写,但重载多个final是可行的。
class Players
{
private int id;
public final void getId(int id)
{
this.id= id;
}
}
class Attackers extends Players
{
public final void getId(int id)
{
this.id= id+1;
}
}
public class Finaltest
{
public static void main(String[] args)
{
Players kupurk = new Players();
kupurk.getId(10);
System.out.println(kupurk.id);
}
}
报错
Finaltest.java:22: 错误: Attackers中的getId(int)无法覆盖Players中的getId(int)
public final void getId(int id)
^
被覆盖的方法为final
所以子类无法继承得到父类final修饰的方法
c. 修饰变量(final主要用于修饰变量)
final修饰的变量,只能赋值一次,并且不能被修改
final修饰的成员属性有两种赋值方式:
①在变量声明的时候初始化;
class Players
{
final int id = 1;
public String toString()
{
return "id = "+ id;
}
}
②在声明变量的时候不赋初值,但是要在这个变量所在的类的所有的构造函数中对这个变量赋初值。
class Players
{
final int id;
public Players()
{
id = 12121;//在构造方法中对final修饰的属性进行赋值
}
public String toString()
{
return "id = "+ id;
}
}
d. final修饰的变量是常量吗?
final 修饰的变量与常量很相似,都只能一次赋值并无法改变。
但final修饰的变量也是有生命期的,如果我们将final修饰在一个局部变量身上,那么这个变量只能作用与一个局部,
所以final修饰的变量是可以提前结束生命的
而const修饰的变量是常量,是全局变量。
但如果用static final修饰 那么这就于常量等价了
11.2 权限修饰符
权限修饰符可以用于修饰 类、成员属性、成员方法
类:只能用public 或者 默认修饰,不能用private和protected修饰
成员属性:被protected修饰的变量在类外可以被访问(但必须是在同一个包中进行访问),
成员方法:
加上默认状态(即缺省)一共有四种
- public
- protected
- private
- 缺省
11.3 抽象(Abstract)
a. 抽象类
前几篇博客我们说过,父类的特征是他所有子类特征的交集。
那么当我们在刚开始写类的时候,我们不知道这个类到底该怎么写,
例如动物叫,我们不知道哪些动物i叫,毕竟每个动物的叫法都不一样。
所以这时候我们可以将类定义为抽象类,日后在子类中进行修饰。
抽象类是用abstract关键字修饰过的类
它的用法是 (public) abstract class 类名{}
抽象类不是具体存在的类,或者说这样的类还不够完善,不能直接使用new关键字调用其构造器生成该类的对象。
抽象类虽然不能实例化对象(不能被构造),类的其它功能依然存在,成员变量、成员方法和构造方法的访问方式和普通类一样。
public class AbstractTest
{
public static void main(String[] args)
{
Friend kupurk = new Friend(kupurk,1);
System.out.println(kupurk);
}
}
abstract class Friend
{
String name;
int id;
public Friend(String name,int id)
{
System.out.println("抽象类构造!");
this.name = name;
this.id = id;
}
public String toString()
{
return "[" + name + ", "+ id + "]";
}
}
编译不通过:
E:\工具\Markdown\输出\EditPlus_out\Abstract and Connector>javac AbstractTest.java
AbstractTest.java:5: 错误: Friend是抽象的; 无法实例化
Friend kupurk = new Friend(kupurk,1);
^
1 个错误
所以抽象类我们无法构造,因此抽象类必须被继承,这也是抽象类存在的意义,用子类去解释抽象类。
ps(抽象类中也有构造方法,但他的用处并不是拿来实例化自己的(因为他压根就没法实例化自己),而是用于将这个特征留给子类,子类并不是抽象的,肯定需要构造啊,需要对父类构造方法进行重写)
abstract不能和哪些关键字共存
a:final 冲突(待补充)
b:private 冲突(待补充)
c:static 无意义(待补充)
a. 抽象方法
用abstract修饰的方法称为 抽象方法。
抽象方法只有方法声明,而没有方法体,因此他不能被调用,只能而且必须要让子类去对抽象方法进行重写。
因为要实现多态,静态绑定时,只能绑定到父类,而父类中的方法是抽象方法,没有方法体,
动态绑定时,子类也没有对该抽象方法进行重写。
最终就会导致编译不通过报错
抽象方法只能存在于抽象类中
在Java中一个没有方法体的方法应该定义为抽象方法 而类中如果有抽象方法,则必须定义为抽象类
抽象方法没有方法体,没有花括号 ”{}“ ,空方法拥有花括号,不要把空方法和抽象方法靠混淆了
11.4 接口(Interface)
接口的概念与抽象类很相似,
不过有一点不相似的是:接口是百分之百的抽象,而抽象类可以不是百分之白的抽象
意思就是说接口内的方法必须全部都是抽象方法,抽象类中的方法运行存在有方法体的方法
又因为接口是抽象的,因此接口和抽象类一样无法实例化
抽象类一定要有一个子类去实现它,接口也一样需要一个类去实现接口
实现方法
(public) class <类名> implements <接口名>;
在这个实现类中,一定要完全实现接口内所有的抽象方法,否则编译不通过
在实现类中可以自己添加除了接口之外的方法,这是可行的
用接口的定义还有包的概念,依葫芦画瓢写了一个我和我朋友恰饭的程序
主类
package com.main;
import com.Users.Kupurk;
import com.Users.Mo;
import com.Users.Sock;
import com.Users.move.ICanjuIplm;
public class Test {
public static void main(String[] args) {
Kupurk kupurk = new Kupurk();
Mo mo = new Mo();
Sock sock = new Sock();
ICanjuIplm icanju = new ICanjuIplm();
icanju.setIperson(kupurk);
icanju.eat();
icanju.setIperson(mo);
icanju.eat();
icanju.setIperson(sock);
icanju.eat();
}
}
用户信息
package com.Users;
public interface Iperson {
void eat();
}
public class Kupurk implements Iperson {
@Override
public void eat()
{
System.out.println(" kupurk 狂吃");
}
}
public class Mo implements Iperson {
@Override
public void eat()
{
System.out.println(" Mo 正在恰饭");
}
}
public class Sock implements Iperson{
@Override
public void eat()
{
System.out.println(" 袜子 正在干饭");
}
}
用户行为
package com.Users.move;
import com.Users.Iperson;
public interface ICanju {
void eat();
}
public class ICanjuIplm implements ICanju
{
private Iperson iperson;
public void setIperson(Iperson iperson)
{
this.iperson = iperson;
}
@Override
public void eat() {
iperson.eat();
}
}
输出结果
成功了,第一次用IDEA分包写代码,泪目