Java——面向对象
1、关键字 static
1.1、产生背景
当我们编写一个类时,其实就是在描述其对象的属性和行为,而并没有产生实质上的对象,只有通过 new 关键字才会产生出对象,这时系统才会分配内存空间给对象,其方法才可以供外部调用。我们有时候希望无论是否产生了对象或无论产生了多少对象的情况下, 某些特定的数据在内存空间里只有一份 ,例如所有的中国人都有个国家名称,每一个中国人都共享这个国家名称,不必在每一个中国人的实例对象中都单独分配一个用于代表国家名称的 变量。
类属性、类方法的设计思想
- 类属性作为该类各个对象之间共享的变量。 在设计类时 分析 哪些属性 不因对象的不同而改变,将这些属性设置为类属性。相应的方法设置为类方法。
- 如果方法与调用者无关,则这样的方法通常被声明为类方法,由于 不需要创建对象就可以调用类方法 ,从而简化了方法 的 调用。
1.2、定义和特征
在类中,用static声明的成员变量为静态成员变量,也成为类变量。类变量的生命周期和类相同,在整个应用程序执行期间都有效。
这里要强调一下:
- static修饰的成员变量和方法,从属于类
- 普通变量和方法从属于对象
- 静态方法不能调用非静态成员,编译会报错
static关键字的用途
- 一句话描述就是:方便在没有创建对象的情况下进行调用(方法/变量)。
- 显然,被static关键字修饰的方法或者变量不需要依赖于对象来进行访问,只要类被加载了,就可以通过类名去进行访问。
- static可以用来修饰类的成员方法、类的成员变量,另外也可以编写static代码块来优化程序性能
1、 类加载的特性
-
在JVM的生命周期里,每个类只会被加载一次。
-
类加载的原则:延迟加载,能少加载就少加载,因为虚拟机的空间是有限的。
2、 类加载的时机
-
1)第一次创建对象要加载类.
-
2)调用静态方法时要加载类,访问静态属性时会加载类。
-
3)加载子类时必定会先加载父类。
-
4)创建对象引用不加载类.
-
5)子类调用父类的静态方法时
- 当子类没有覆盖父类的静态方法时,只加载父类,不加载子类
- 当子类有覆盖父类的静态方法时,既加载父类,又加载子类
-
6)访问静态常量,如果编译器可以计算出常量的值,则不会加载类,例如:
public static final int a =123
;否则会加载类,例如:public static final int a = math.PI
。
1.3、使用
使用 范围:
- 在 Java 类中 可用 static 修饰 属性 、 方法 、 代码块 、 内部类
被 修饰后的成员具备以下特点:
- 随着类的加载而加载
- 优先于对象存在
- 修饰的成员,被所有对象所共享
- 访问权限允许时,可不创建对象,直接被类调用
package com.atguigu.java;
/*
* static关键字的使用
*
* 1、static:静态的
* 2、static可以用来修饰:属性、方法、代码块、内部类
*
* 3、使用static来修饰属性:静态变量(或类变量)
* (1)属性,按属否使用static修饰,又分为:静态属性vs非静态属性
* 实例变量:我们创建了类的多个对象,每个对象都独立的拥有一套类的实例变量。当修改一个对象中的非静态属性时,不会导致其他对象中同样的属性值的修改。
* 静态变量:我们创建了类的多个对象,多个对象共享同一个静态变量,当通过某一个对象修改静态变量时,会导致其他对象调用此静态变量时,是修改过的。
* (2)static修饰属性的其他说明:静态变量随着类的加载而加载、静态变量的加载要早于对象的创建、可以通过"类.静态变量"的方式进行调用、由于类只会加载一次,则静态变量在内存中也只会存在一份:存在方法区的静态域中。
* (3)类变量 实例变量
* 类 yes no
* 对象 yes yes
* (4)静态属性举例:System.out;Math.PI
* 方法区:类的加载信息、静态域、常量池。
*
* 4、使用static来修饰方法:静态方法
* (1)随着类的加载而加载,可以通过"类.静态方法"的方式调用。
* (2)静态方法 实例方法
* 类 yes no
* 对象 yes yes
* (3)静态方法中,只能调用静态的方法或属性。
* 非静态方法中,既可以调用非静态的方法或属性,也可以调用静态的方法或属性。
*
*5.static注意点:
* 在静态的方法内,不能使用this关键字、super关键字。
*
*6.开发中,如何确定一个属性是否要声明为static的?
* (1)属性可以被多个对象所共享的,不会随着对象的不同而不同的。
*
*7.开发中,如何确定一个方法是否要声明为static的?
* (1)操作静态属性的方法,通常都设置为静态的。
* (2)工具类中的方法,习惯上声明为静态的,比如:Math,Arrays,Collections
*/
public class StaticTest {
public static void main(String[] args) {
Chinese c1 = new Chinese();
c1.name = "姚明";
c1.age = 40;
Chinese c2 = new Chinese();
c2.name = "马龙";
c2.age = 35;
c1.nation = "CHN";
System.out.println(c2.nation);
}
}
// 中国人
class Chinese{
String name;
int age;
static String nation;
}
package com.atguigu.exer1;
public class Account {
private int id;
private String pwd = "000000";
private double balance;
private static double interestRate;
private static double minMoney = 1.0;
private static int init = 1001; // 用于自动生成id
public String getPwd(){
return this.pwd;
}
public void setPwd(String pwd){
this.pwd = pwd;
}
public double getBalance(){
return this.balance;
}
public static double getInterestRate(){
return Account.interestRate;
}
public static void setInterestRate(double interestRate){
Account.interestRate = interestRate;
}
public static double getMinMoney(){
return Account.minMoney;
}
public static void setMinMoney(double minMoney){
Account.minMoney = minMoney;
}
public Account(){
id = init++;
}
public Account(String pwd, double balance){
id = init++;
this.pwd = pwd;
this.balance = balance;
}
public String toString(){
return "" + id + pwd + balance;
}
}
1.3.1、static成员变量
虽然java语言中没有全局的概念,但可以通过static关键字来达到全局的效果。java类提供了两种类型的变量:用static关键字修饰的静态变量和没有static关键字修饰的实例变量。
静态变量属于类,在内存中只有一个副本(所有势力都指向同一个内存地址)。只要静态变量所在的类被加载,这个静态类就会被分配空间,因此就可以被使用。对静态变量的引用有两种方式,分别为“类.静态变量”和“对象.静态变量”。
实例变量属于对象,只有对象被创建后,实例变量才会被分配空间,才能被使用,他在内存中存在多个副本,只能用“对象.静态变量”的方式来引用。
静态变量只有一个,被类所拥有,所有的对象都共享这个静态变量,而实例对象与具体对象有关。
1.3.2、 static成员方法
没有对象的实例时,可以用 类名.方法名
的形式访问 由 static 修饰 的 类方法。
与变量类似,java类同时也提供了static方法与非static方法。static方法是类的方法,不需要创建对象就可以被使用,而非static方法是对象的方法,只有对象被创建出来后才可以被使用。
static 方法中不能使用this和super关键字,不能调用非static方法,只能访问所属类的静态成员变量和成员方法,因为当static方法被调用的时候,这个类的对象可能还没有被创建,即使已经被创建,也无法确定调用哪个对象的方法。同理static方法也不能访问非static类型的变量。
static的一个很重要的的用途的是实现单例模式。单例模式的特点是该类只能有一个实例,为了实现这功能,必须隐藏类的构造方法,即把构造方法申明为private,并提供一个创建对象的方法。由于构造方法被申明为private,外界无法直接创建这个类型对象,只能通过该类提供的方法来获取类的对象,要达到这样的目的只能把创建
对象的方法生明为static:
class Singleton{
private static Singleton instance=null;
private Singleton(){}
public static Singleton getInstance(){
if (instance=null){
instance=new Singleton();
}
return instance;
}
}
1.3.2.1、单例设计模式(singleton)
- 设计 模式 是在大量的实践中总结和理论化之后优选的代码结构、编程风格、以及解决问题的思考方式 。 设计模免去我们自己再思考和摸索 。就像是经典的棋谱,不同的棋局,我们用不同 的 棋谱。 套路
- 所谓 类的单例设计模式,就是采取一定的方法保证在整个的软件系统中,对某个类 只能存在一个对象实例 ,并且该类只提供一个取得其对象实例的方法。如果我们要让类在一个虚拟机中只能产生一个对象,我们首先必须将类 的 构造 器 的 访问权限设置为 private ,这样,就不能用 new 操作符在类的外部产生类的对象了,但在类内部仍可以产生该类的对象。因为在类的外部开始还无法得到类的对象,只能 调用该类的某个静态方法 以返回类内部创建的对象,静态方法只能访问类中的静态成员变量,所以,指向类内部产生的 该类对象的变量也必须定义成静态的 。
package com.atguigu.java;
/*
一、单例设计模式。
1、定义:所谓类的单例设计模式,就是采取一定的方法保证在整个的软件系统中,对某个类只能存在一个对象实例。
2、如何实现?
3、区分俄汉式和懒汉式
俄汉式:坏处:对象加载时间过程。线程安全。
懒汉式:好处:延迟对象的创建。目前写法线程不安全。
*/
public class SingletonTest {
public static void main(String[] args){
Bank bank1 = Bank.getInstance();
Bank bank2 = Bank.getInstance();
System.out.println(bank1 == bank2);
}
}
//饿汉式
class Bank{
//1.私有化类的构造器
private Bank(){
}
//2.内部创建类的对象
//4.要求此属性也必须声明为静态的
private static Bank instance = new Bank();
//3.提供公共的静态方法,返回类的对象
public static Bank getInstance(){
return instance;
}
}
package com.atguigu.java;
/*
单例模式的懒汉式实现
*/
public class SingletonTest2 {
public static void main(String[] args){
Order order1 = Order.getInstance();
Order order2 = Order.getInstance();
System.out.println(order1 == order2);
}
}
class Order{
//1.私有化类的构造器
private Order(){
}
//2.声明当前类对象,没有初始化。
//4.此对象也必须声明为static的。
private static Order instance = null;
//3.声明public、static的返回当前类对象的方法
public static Order getInstance(){
if(instance == null){
instance = new Order();
}
return instance;
}
}
单例设计模式应用场景
2、理解 main 方法的语法
- 由于 Java 虚拟机需要调用类的 main() 方法,所以该方法的访问权限必须是public ,又 因为 Java 虚拟机在执行main() 方法时不必创建对象,所以该方法必须是 static 的,该方法接收一个 String 类型的数组参数,该数组中 保存 执行Java 命令 时传递给所运行的类的参数 。
- 又因为 main () 方法是静态的 ,我们不能直接访问该类中的非静态成员,必须创建该类的一个实例对象后,才能通过这个对象去访问类中的非静态成员,这种情况,我们 在 之前 的 例子中多次 碰到 。
package com.atguigu.java2;
/*
* main()方法的使用说明:
* 1. main()方法作为程序的入口
* 2. main()方法也是一个普通的静态方法
* 3. main()方法可以作为我们与控制台交互的方式。(之前:使用Scanner)
*
*
*
*/
public class MainTest {
public static void main(String[] args) {//入口
Main.main(new String[100]);
MainTest test = new MainTest();
test.show();
}
public void show(){
}
}
class Main{
public static void main(String[] args) {
for(int i = 0;i < args.length;i++){
args[i] = "args_" + i;
System.out.println(args[i]);
}
}
}
3、类 的成员之 四 :代码块
代码 块 或初始化块 的作用
- 对 Java 类或对象进行 初始化
代码块 或初始化块 的分类:
-
一个类中代码块若有修饰符 则只能被 static 修饰 称为
静态代码块(static block)
,没有使用 static 修饰的 为非静态代码块
。
静态代码块:用 static 修饰的代码块 -
1.可以 有输出语句。
-
2.可以 对类的属性、类的声明进行初始化操作。
-
3.不 可以对非静态的属性初始化。即:不可以调用非静态 的 属性 和方法。
-
4.若 有多个静态的代码块,那么按照从上到下的顺序依次执行。
-
5.静态 代码块的执行要先于非静态代码块。
-
6.静态 代码 块随着类的加载而加载,且只执行 一 次。
非静态代码块:没有 static 修饰的代码块
-
1、可以 有输出语句 。
-
2、可以 对类的属性 、 类的声明进行初始化操作 。
-
3、除了调用非静态的结构外 还可以调用 静态的变量或方法 。
-
4、若 有多个非静态的代码块 那么按照从上到下的 顺序 依次 执行 。
-
5、每次 创建对象的时候 都会执行一次 。 且先于构造 器 执行 。
总结:程序中成员变量赋值的执行顺序
扩展:对象可以赋值的位置
- 1、默认初始化
- 2、显示初始化
- 3、构造器中初始化
- 4、有了对象以后,可以通过
对象.属性
或者对象.方法
,进行赋值 - 5、在代码块中赋值
package com.atguigu.java3;
/*
* 类的成员之四:代码块(或初始化块)
*
* 1. 代码块的作用:用来初始化类、对象
* 2. 代码块如果有修饰的话,只能使用static.
* 3. 分类:静态代码块 vs 非静态代码块
*
* 4. 静态代码块
* >内部可以有输出语句
* >随着类的加载而执行,而且只执行一次
* >作用:初始化类的信息
* >如果一个类中定义了多个静态代码块,则按照声明的先后顺序执行
* >静态代码块的执行要优先于非静态代码块的执行
* >静态代码块内只能调用静态的属性、静态的方法,不能调用非静态的结构
*
* 5. 非静态代码块
* >内部可以有输出语句
* >随着对象的创建而执行
* >每创建一个对象,就执行一次非静态代码块
* >作用:可以在创建对象时,对对象的属性等进行初始化
* >如果一个类中定义了多个非静态代码块,则按照声明的先后顺序执行
* >非静态代码块内可以调用静态的属性、静态的方法,或非静态的属性、非静态的方法
*
*/
public class BlockTest {
public static void main(String[] args) {
String desc = Person.desc;
System.out.println(desc);
Person p1 = new Person();
Person p2 = new Person();
System.out.println(p1.age);
Person.info();
}
}
class Person{
//属性
String name;
int age;
static String desc = "我是一个人";
//构造器
public Person(){
}
public Person(String name,int age){
this.name = name;
this.age = age;
}
//非static的代码块
{
System.out.println("hello, block - 2");
}
{
System.out.println("hello, block - 1");
//调用非静态结构
age = 1;
eat();
//调用静态结构
desc = "我是一个爱学习的人1";
info();
}
//static的代码块
static{
System.out.println("hello,static block-2");
}
static{
System.out.println("hello,static block-1");
//调用静态结构
desc = "我是一个爱学习的人";
info();
//不可以调用非静态结构
// eat();
// name = "Tom";
}
//方法
public void eat(){
System.out.println("吃饭");
}
@Override
public String toString() {
return "Person [name=" + name + ", age=" + age + "]";
}
public static void info(){
System.out.println("我是一个快乐的人!");
}
package com.atguigu.java3;
//总结:由父及子,静态先行
class Root{
static{
System.out.println("Root的静态初始化块");
}
{
System.out.println("Root的普通初始化块");
}
public Root(){
super();
System.out.println("Root的无参数的构造器");
}
}
class Mid extends Root{
static{
System.out.println("Mid的静态初始化块");
}
{
System.out.println("Mid的普通初始化块");
}
public Mid(){
super();
System.out.println("Mid的无参数的构造器");
}
public Mid(String msg){
//通过this调用同一类中重载的构造器
this();
System.out.println("Mid的带参数构造器,其参数值:"
+ msg);
}
}
class Leaf extends Mid{
static{
System.out.println("Leaf的静态初始化块");
}
{
System.out.println("Leaf的普通初始化块");
}
public Leaf(){
//通过super调用父类中有一个字符串参数的构造器
super("尚硅谷");
System.out.println("Leaf的构造器");
}
}
public class LeafTest{
public static void main(String[] args){
new Leaf();
System.out.println();
new Leaf();
}
}
4、关键字: final
package com.atguigu.java3;
/*
* final:最终的
*
* 1. final可以用来修饰的结构:类、方法、变量
*
* 2. final 用来修饰一个类:此类不能被其他类所继承。
* 比如:String类、System类、StringBuffer类
*
* 3. final 用来修饰方法:表明此方法不可以被重写
* 比如:Object类中getClass();
*
* 4. final 用来修饰变量:此时的"变量"就称为是一个常量
* 4.1 final修饰属性:可以考虑赋值的位置有:显式初始化、代码块中初始化、构造器中初始化
* 4.2 final修饰局部变量:
* 尤其是使用final修饰形参时,表明此形参是一个常量。当我们调用此方法时,给常量形参赋一个实参。一旦赋值
* 以后,就只能在方法体内使用此形参,但不能进行重新赋值。
*
* static final 用来修饰属性:全局常量
*/
public class FinalTest {
final int WIDTH = 0;
final int LEFT;
final int RIGHT;
// final int DOWN;
{
LEFT = 1;
}
public FinalTest(){
RIGHT = 2;
}
public FinalTest(int n){
RIGHT = n;
}
// public void setDown(int down){
// this.DOWN = down;
// }
public void doWidth(){
// width = 20;
}
public void show(){
final int NUM = 10;//常量
// NUM += 20;
}
public void show(final int num){
// num = 20;//编译不通过
System.out.println(num);
}
public static void main(String[] args) {
int num = 10;
num = num + 5;
FinalTest test = new FinalTest();
// test.setDown(3);
test.show(10);
}
}
final class FinalA{
}
//class B extends FinalA{
//
//}
//class C extends String{
//
//}
class AA{
public final void show(){
}
}
class BB extends AA{
// public void show(){
//
// }
}
5、抽象类与抽象方法
抽象类与抽象方法随着继承层次中一个个新子类的定义,类变得越来越具体,而父类则更一般,更通用。类的设计应该保证父类和子类能够共享特征。有时将一个父类设计得非常抽象,以至于它没有具体的实例,这样的类叫做 抽象类
。
- 用 abstract 关键字来修饰一 个 类, 这个类叫做 抽象 类 。
- 用 abstract 来修饰一 个 方法, 该方法叫做 抽象方法 。
- 抽象方法:只有方法的声明,没有方法的实现。以分号 结束:
比如:
public abstract void talk()
- 含有抽象方法 的类必须被声明为抽象类。
- 抽象 类不能被实例化。抽象类是用来被继承的,抽象类的子类必须重写父类的抽象方法,并提供方法体。若没有重写全部的抽象方法,仍为抽象类。
- 不能 用 abstract 修饰变量、代码块、构造器;
- 不能用 abstract 修饰 私有方法、静态方法、 final 的 方法、 final 的类。
package com.atguigu.java;
/*
* abstract关键字的使用
* 1.abstract:抽象的
* 2.abstract可以用来修饰的结构:类、方法
*
* 3. abstract修饰类:抽象类
* > 此类不能实例化
* > 抽象类中一定有构造器,便于子类实例化时调用(涉及:子类对象实例化的全过程)
* > 开发中,都会提供抽象类的子类,让子类对象实例化,完成相关的操作
*
*
* 4. abstract修饰方法:抽象方法
* > 抽象方法只有方法的声明,没有方法体
* > 包含抽象方法的类,一定是一个抽象类。反之,抽象类中可以没有抽象方法的。
* > 若子类重写了父类中的所有的抽象方法后,此子类方可实例化
* 若子类没有重写父类中的所有的抽象方法,则此子类也是一个抽象类,需要使用abstract修饰
*/
public class AbstractTest {
public static void main(String[] args) {
//一旦Person类抽象了,就不可实例化
// Person p1 = new Person();
// p1.eat();
}
}
abstract class Creature{
public abstract void breath();
}
abstract class Person extends Creature{
String name;
int age;
public Person(){
}
public Person(String name,int age){
this.name = name;
this.age = age;
}
//不是抽象方法:
// public void eat(){
//
// }
//抽象方法
public abstract void eat();
public void walk(){
System.out.println("人走路");
}
}
class Student extends Person{
public Student(String name,int age){
super(name,age);
}
public Student(){
}
public void eat(){
System.out.println("学生多吃有营养的食物");
}
@Override
public void breath() {
System.out.println("学生应该呼吸新鲜的没有雾霾的空气");
}
}
抽象类的匿名子类
package com.atguigu.java;
/*
* 抽象类的匿名子类
*
*/
public class PersonTest {
public static void main(String[] args) {
method(new Student());//匿名对象
Worker worker = new Worker();
method1(worker);//非匿名的类非匿名的对象
method1(new Worker());//非匿名的类匿名的对象
System.out.println("********************");
//创建了一匿名子类的对象:p
Person p = new Person(){
@Override
public void eat() {
System.out.println("吃东西");
}
@Override
public void breath() {
System.out.println("好好呼吸");
}
};
method1(p);
System.out.println("********************");
//创建匿名子类的匿名对象
method1(new Person(){
@Override
public void eat() {
System.out.println("吃好吃东西");
}
@Override
public void breath() {
System.out.println("好好呼吸新鲜空气");
}
});
}
public static void method1(Person p){
p.eat();
p.breath();
}
public static void method(Student s){
}
}
class Worker extends Person{
@Override
public void eat() {
}
@Override
public void breath() {
}
}
6、接口 (interface)
- 一方面 有时 必须从几个类中派生出一个子类 继承它们所有的属性和方法 。 但是 Java 不支持多重继承 。 有了接口
就可以得到多重继承的 效果 。 - 另一方面, 有时 必须从几个类中抽取出一些共同的行为特征,而它们之间又没有 is a 的关系,仅仅是具有相同的行为特征而已 。例如:鼠标、键盘、打印机、扫描仪、摄像头、充电器、 MP3 机、手机、数码相机、移动硬盘等都 支持 USB 连接 。
- 接口 就是规范,定义的是一组规则,体现了现实世界中“如果你是 要 则必须能 …”的思想。 继承是一个 是不是
的关系,而接口实现则是 能不能的关系。 - 接口 的本质是契约,标准,规范 ,就像我们的法律一样。制定好后大家都要遵守 。
接口 interface 是抽象方法
和常量值
定义的集合
接口的特点:
- 用 interface 来定义 。
- 接口中的所有成员变量都 默认 是由 public static final 修饰的 。
- 接口中 的所有抽象方法 都 默认 是由 public abstract 修饰的 。
- 接口中没有 构造器 。
- 接口采用多继承机制 。
定义 Java 类的语法格式: 先写 extends ,后写 implements
class SubClass extends SuperClass implements InterfaceA{ }
- 一 个类可以实现多个接口 接口也可以继承其它 接口 。
- 实现接口的类中必须提供接口中所有方法的具体实现内容,方可实例化。否则,仍为抽象类。
- 接口的主要用途就是被实现类实现。 (面向接口编程)
- 与继承关系类似,接口与实现类之间存在 多态性
- 接口 和类 是 并列关系 或者可以理解为一种特殊的 类 。 从 本质上讲,接口是一种特殊的抽象类,这种抽象类中 只包含常量和方法的定义(JDK7.0 及之前 而没有变量和方法的实现。
package com.atguigu.java1;
/*
* 接口的使用
* 1.接口使用interface来定义
* 2.Java中,接口和类是并列的两个结构
* 3.如何定义接口:定义接口中的成员
*
* 3.1 JDK7及以前:只能定义全局常量和抽象方法
* >全局常量:public static final的.但是书写时,可以省略不写
* >抽象方法:public abstract的
*
* 3.2 JDK8:除了定义全局常量和抽象方法之外,还可以定义静态方法、默认方法(略)
*
* 4. 接口中不能定义构造器的!意味着接口不可以实例化
*
* 5. Java开发中,接口通过让类去实现(implements)的方式来使用.
* 如果实现类覆盖了接口中的所有抽象方法,则此实现类就可以实例化
* 如果实现类没有覆盖接口中所有的抽象方法,则此实现类仍为一个抽象类
*
* 6. Java类可以实现多个接口 --->弥补了Java单继承性的局限性
* 格式:class AA extends BB implements CC,DD,EE
*
* 7. 接口与接口之间可以继承,而且可以多继承
*
* *******************************
* 8. 接口的具体使用,体现多态性
* 9. 接口,实际上可以看做是一种规范
*
* 面试题:抽象类与接口有哪些异同?
*
*/
public class InterfaceTest {
public static void main(String[] args) {
System.out.println(Flyable.MAX_SPEED);
System.out.println(Flyable.MIN_SPEED);
// Flyable.MIN_SPEED = 2;
Plane plane = new Plane();
plane.fly();
}
}
interface Flyable{
//全局常量
public static final int MAX_SPEED = 7900;//第一宇宙速度
int MIN_SPEED = 1;//省略了public static final
//抽象方法
public abstract void fly();
//省略了public abstract
void stop();
//Interfaces cannot have constructors
// public Flyable(){
//
// }
}
interface Attackable{
void attack();
}
class Plane implements Flyable{
@Override
public void fly() {
System.out.println("通过引擎起飞");
}
@Override
public void stop() {
System.out.println("驾驶员减速停止");
}
}
abstract class Kite implements Flyable{
@Override
public void fly() {
}
}
class Bullet extends Object implements Flyable,Attackable,CC{
@Override
public void attack() {
// TODO Auto-generated method stub
}
@Override
public void fly() {
// TODO Auto-generated method stub
}
@Override
public void stop() {
// TODO Auto-generated method stub
}
@Override
public void method1() {
// TODO Auto-generated method stub
}
@Override
public void method2() {
// TODO Auto-generated method stub
}
}
//************************************
interface AA{
void method1();
}
interface BB{
void method2();
}
interface CC extends AA,BB{
}
接口和抽象类之间 的对比
Java 8中关于接口的改进
Java 8中,你可以为接口添加 静态方法 和 默认方法 。从技术角度来说,这是完全合法的,只是它看起来违反了接口作为一个抽象定义的理念。
- 静态方法:使用 static 关键字修饰。 可以通过接口直接调用静态方法,并执行其方法体。我们经常在相互一起使用的类中使用静态方法。你可以在标准库中找到像 Collection/Collections 或者 Path/Paths 这样成对的接口和类。
- 默认方法:默认方法使用 default关键字修饰。可以通过实现类对象来调用。我们在已有的接口中提供新方法的同时,还保持了与旧版本代码的兼容性。比如:java 8 API 中对Collection 、 List 、 Comparator 等接口提供了丰富的默认方法。
接口中的默认方法
- 若 一个接口中定义了一个默认方法,而另外一 个接口中也定义 了一个同名同参数的 方法(不管此方法 是否是默认方法),在实现 类同时实现了这两个接口时,会出现: 接口冲突。
解决办法:实现类必须覆盖接口中同名同参数的方法,来解决冲突
。
- 若一个接口中定义了一个默认方法 ,而父类中也定义了一个同名同参数的非抽象方法,则不会出现冲突问题。因为此时遵守: 类优先原则。接口中具有相同名称和参数的默认方法会被忽略 。
举例
package com.atguigu.java8;
public class SubClassTest {
public static void main(String[] args) {
SubClass s = new SubClass();
// s.method1();
// SubClass.method1();
//知识点1:接口中定义的静态方法,只能通过接口来调用。
CompareA.method1();
//知识点2:通过实现类的对象,可以调用接口中的默认方法。
//如果实现类重写了接口中的默认方法,调用时,仍然调用的是重写以后的方法
s.method2();
//知识点3:如果子类(或实现类)继承的父类和实现的接口中声明了同名同参数的默认方法,
//那么子类在没有重写此方法的情况下,默认调用的是父类中的同名同参数的方法。-->类优先原则
//知识点4:如果实现类实现了多个接口,而这多个接口中定义了同名同参数的默认方法,
//那么在实现类没有重写此方法的情况下,报错。-->接口冲突。
//这就需要我们必须在实现类中重写此方法
s.method3();
}
}
class SubClass extends SuperClass implements CompareA,CompareB{
public void method2(){
System.out.println("SubClass:上海");
}
public void method3(){
System.out.println("SubClass:深圳");
}
//知识点5:如何在子类(或实现类)的方法中调用父类、接口中被重写的方法
public void myMethod(){
method3();//调用自己定义的重写的方法
super.method3();//调用的是父类中声明的
//调用接口中的默认方法
CompareA.super.method3();
CompareB.super.method3();
}
}
public interface CompareA {
//静态方法
public static void method1(){
System.out.println("CompareA:北京");
}
//默认方法
public default void method2(){
System.out.println("CompareA:上海");
}
default void method3(){
System.out.println("CompareA:上海");
}
}
public interface CompareB {
default void method3(){
System.out.println("CompareB:上海");
}
}
public class SuperClass {
public void method3(){
System.out.println("SuperClass:北京");
}
}
7、内部类
- 当一个事物的内部,还有一个部分需要一个完整的结构进行描述,而这个内部的完整的结构又只为外部事物提供服务,那么整个内部的完整结构最好使用内部类 。
- 在 Java 中,允许一个类的定义位于另一个类的内部,前者称为 内部类 ,后者称为 外部类 。
- Inner class 一般用在定义它的类或语句块之内,在外部引用它时必须给出完整的名称。
- Inner class 的名字不能与包含 它 的 外部类 类 名相同;
- 分类:
成员内部类
(static 成员内部类和非 static 成员内部类),局部内部类
(不谈修饰符)、匿名内部类
成员内部类
成员内部类作为 类 的 成员的角色:
- 和 外部类不同, Inner class 还可以声明 为 private 或 protected
- 可以调用外部类的结构
- Inner class 可以声明为 static 的 但此时就不能再使用外层类的非 static 的成员变量;
成员内部类 作为类的角色:
-
可以在内部定义属性 、 方法 、 构造器等结构
-
可以 声明为 abstract 类, 因此可以被其它的内 部类 继承
-
可以声明为 final 的
【注意 】
-
1.非 static 的成员内 部类中的成员不能声明为 static 的 ,只有在外部类或 static 的成员内 部类中才可声明 static 成员 。
-
2.外部类 访问 成员 内 部类的成员 ,需要
内部类.成员
或内部类对象.成员
的 方式 -
3.成员内部类可以直接使用外部类的所有成员 包括私有的数据
-
4.当想要在外部类的静态成员部分使用内部 类时 ,可以考虑内部类 声明为静态的
package com.atguigu.java2;
/*
* 类的内部成员之五:内部类
* 1. Java中允许将一个类A声明在另一个类B中,则类A就是内部类,类B称为外部类
*
* 2.内部类的分类:成员内部类(静态、非静态) vs 局部内部类(方法内、代码块内、构造器内)
*
* 3.成员内部类:
* 一方面,作为外部类的成员:
* >调用外部类的结构
* >可以被static修饰
* >可以被4种不同的权限修饰
*
* 另一方面,作为一个类:
* > 类内可以定义属性、方法、构造器等
* > 可以被final修饰,表示此类不能被继承。言外之意,不使用final,就可以被继承
* > 可以被abstract修饰
*
*
* 4.关注如下的3个问题
* 4.1 如何实例化成员内部类的对象
* 4.2 如何在成员内部类中区分调用外部类的结构
* 4.3 开发中局部内部类的使用 见《InnerClassTest1.java》
*
*/
public class InnerClassTest {
public static void main(String[] args) {
//创建Dog实例(静态的成员内部类):
Person.Dog dog = new Person.Dog();
dog.show();
//创建Bird实例(非静态的成员内部类):
// Person.Bird bird = new Person.Bird();//错误的
Person p = new Person();
Person.Bird bird = p.new Bird();
bird.sing();
System.out.println();
bird.display("黄鹂");
}
}
class Person{
String name = "小明";
int age;
public void eat(){
System.out.println("人:吃饭");
}
//静态成员内部类
static class Dog{
String name;
int age;
public void show(){
System.out.println("卡拉是条狗");
// eat();
}
}
//非静态成员内部类
class Bird{
String name = "杜鹃";
public Bird(){
}
public void sing(){
System.out.println("我是一只小小鸟");
Person.this.eat();//调用外部类的非静态属性
eat();
System.out.println(age);
}
public void display(String name){
System.out.println(name);//方法的形参
System.out.println(this.name);//内部类的属性
System.out.println(Person.this.name);//外部类的属性
}
}
public void method(){
//局部内部类
class AA{
}
}
{
//局部内部类
class BB{
}
}
public Person(){
//局部内部类
class CC{
}
}
}
如何声明局部内部类
局部内部类的特点
- 内 部类仍然是一个独立的类,在编译之后内部类会被编译成独立的 .class 文件,但是前面冠以外部类的类名和$符号,以及数字编号。
- 只能在声明它的方法或代码块中使用,而且是先声明后使用。除此之外的任何地方都不能使用该类。
- 局部内部类可以使用外部类的成员,包括私有 的 。
- 局部内部类可以使用外部方法的局部变量,但是必须是 final 的。 由局部内部类和局部变量的声明周期不同所致。
- 局部内部类和局部变量地位类似,不能使用 public,protected, 缺省 ,private
- 局部内部类不能使用 static 修饰,因此也不能包含静态 成员
匿名内部类
匿名内部类不能定义任何静态成员、方法和类,只能创建匿名内部类的一个实例。一个匿名内部类一定是在 new 的后面,用其隐含实现一个接口或实现一个类。
匿名内部类的特点
- 匿名内部类必须继承父类或实现接口
- 匿名内部类只能有一个对象
- 匿名内部类对象只能使用多态形式引用