面向对象 4

1 抽象类和接口

1.1 抽象方法和抽象类

·抽象方法
使用abstract修饰的方法,没有方法体,只有声明。定义的是一种“规范”,就是告 诉子类必须要给抽象方法提供具体的实现。
·抽象类
包含抽象方法的类就是抽象类。通过 abstract 方法定义规范,然后要求子类必须定义 具体实现。通过抽象类,我们就可以做到严格限制子类的设计,使子类之间更加通用。
抽象类和抽象方法的基本用法代码如下:

package com.txw.test;

// 抽象类 
abstract class Animal {
    abstract public void shout(); 	// 抽象方法 
}
class Dog extends Animal {
    // 子类必须实现父类的抽象方法,否则编译错误
    public void shout() {
        System.out.println("汪汪汪!");
    }

    public void seeDoor(){
        System.out.println("看门中....");
    }
}
// 测试抽象类 
public class TestAbstractClass {
    public static void main(String[ ] args) {
        Dog a = new Dog();
        a.shout();
        a.seeDoor();
    }
}
抽象类的使用要点: 
1.有抽象方法的类只能定义成抽象类 
2.抽象类不能实例化,即不能用 new 来实例化抽象类。 
3.抽象类可以包含属性、方法、构造方法。但是构造方法不能用来 new 实例, 只能用来被子类调用。 
4.抽象类只能用来被继承。 
5.抽象方法必须被子类实现。

1.2 接口 interface

接口就是规范,定义的是一组规则,体现了现实世界中“如果你是…则必须能…”的思 想。如果你是天使,则必须能飞。如果你是汽车,则必须能跑。如果你是好人,则必须能干掉坏人;如果你是坏人,则必须欺负好人。
接口的本质是契约,就像我们人间的法律一样。制定好后大家都遵守。
面向对象的精髓,是对对象的抽象,最能体现这一点的就是接口。为什么我们讨论设计 模式都只针对具备了抽象能力的语言(比如 C++、Java、C#等),就是因为设计模式所研 究的,实际上就是如何合理的去抽象。

1.2.1 接口的作用

为什么需要接口?接口和抽象类的区别?
接口就是比“抽象类”还“抽象”的“抽象类”,可以更加规范的对子类进行约束。全 面地专业地实现了:规范和具体实现的分离。
抽象类还提供某些具体实现,接口不提供任何实现,接口中所有方法都是抽象方法。接 口是完全面向规范的,规定了一批类具有的公共方法规范。
从接口的实现者角度看,接口定义了可以向外部提供的服务。
从接口的调用者角度看,接口定义了实现者能提供那些服务。
接口是两个模块之间通信的标准,通信的规范。如果能把你要设计的模块之间的接口定 义好,就相当于完成了系统的设计大纲,剩下的就是添砖加瓦的具体实现了。大家在工作以 后,做系统时往往就是使用“面向接口”的思想来设计系统。
接口和实现类不是父子关系,是实现规则的关系。比如:我定义一个接口 Runnable, Car 实现它就能在地上跑,Train 实现它也能在地上跑,飞机实现它也能在地上跑。就是说, 如果它是交通工具,就一定能跑,但是一定要实现 Runnable 接口。

区别:
1.普通类:具体实现。
2.抽象类:具体实现,规范(抽象方法)3.接口:规范!

1.2.2 如何定义和使用接口(JDK8 以前)

声明格式:

[访问修饰符] interface 接口名 [extends 父接口 1,父接口 2] { 
	常量定义; 方法定义; 
}

定义接口的详细说明

1.访问修饰符:只能是 public 或默认。 
2.接口名:和类名采用相同命名机制。 
3.extends:接口可以多继承。 
4.常量:接口中的属性只能是常量,总是:public static final 修饰。不写也是。
5.方法:接口中的方法只能是:public abstract。 省略的话,也是 public abstract

要点

1.子类通过 implements 来实现接口中的规范。 
2.接口不能创建实例,但是可用于声明引用变量类型。 
3.一个类实现了接口,必须实现接口中所有的方法,并且这些方法只能是 public 的。 
4.JDK1.8(不含 8)之前,接口中只能包含静态常量、抽象方法,不能有普通属性、构造 方法、普通方法。
5.JDK1.8(含 8)后,接口中包含普通的静态方法、默认方法。

接口的使用代码如下:

package com.txw.test;

public class TestInterface {
    public static void main(String[ ] args) {
        Volant volant = new Angel();
        volant.fly();
        System.out.println(Volant.FLY_HIGHT);
        Honest honest = new GoodMan();
        honest.helpOther();
    }
}
/**飞行接口*/
interface Volant {
    int FLY_HIGHT = 100; // 总是:public static final类型的;
    void fly(); // 总是:public abstract void fly(); 
}
/**善良接口*/
interface Honest {
    void helpOther();
}
/**Angel类实现飞行接口和善良接口*/
class Angel implements Volant, Honest{
    public void fly() {
        System.out.println("我是天使,飞起来啦!");
    }

    public void helpOther() {
        System.out.println("扶老奶奶过马路!");
    }
}
class GoodMan implements Honest {
    public void helpOther() {
        System.out.println("扶老奶奶过马路!");
    }
}
class BirdMan implements Volant {
    public void fly() {
        System.out.println("我是鸟人,正在飞!");
    }
}

执行几点为如图所示:在这里插入图片描述

1.2.3 接口中定义静态方法和默认方法(JDK8 以后)

JAVA8 之前,接口里的方法要求全部是抽象方法。
JAVA8(含 8)之后,以后允许在接口里定义默认方法和类方法。
1. 默认方法
Java 8 及以上新版本,允许给接口添加一个非抽象的方法实现,只需要使用 default 关 键字即可,这个特征又叫做默认方法(也称为扩展方法)。
默认方法和抽象方法的区别是抽象方法必须要被实现,默认方法不是。作为替代方式,接口可以提供默认方法的实现,所有这个接口的实现类都会通过继承得到这个方法。
演示的代码如下:

package com.txw.test;

public class Test {
   public static void main(String[] args) {
       A a = new Test_A();
       a.moren();
   }
}
interface A {
   default void moren(){
       System.out.println("我是接口A中的默认方法!");
   }
}
class Test_A implements A {
   @Override
   public void moren() {
       System.out.println("Test_A.moren");
   }
}

2. 静态方法
JAVA8 以后,我们也可以在接口中直接定义静态方法的实现。这个静态方法直接从属 于接口(接口也是类,一种特殊的类),可以通过接口名调用。 如果子类中定义了相同名字的静态方法,那就是完全不同的方法了,直接从属于子类。 可以通过子类名直接调用。

package com.txw.test;

public class Test {
   public static void main(String[] args) {
       A.staticMethod();
       Test_A.staticMethod();
   }
}
interface A {
   public static void staticMethod(){
       System.out.println("A.staticMethod");
   }
}
class Test_A implements A {
   public static void staticMethod(){
       System.out.println("Test_A.staticMethod");
   }
}

3. 静态方法和默认方法
本接口的默认方法中可以调用静态方法。

package com.txw.test;

public class Test {
    public static void main(String[] args) {
        A a = new Test_A();
        a.moren();
    }
}
interface A {
    public static void staticMethod(){
        System.out.println("A.staticMethod");
    }
    public default void moren(){
        staticMethod();
        System.out.println("A.moren");
    }
}
class Test_A implements A {
    public static void staticMethod(){
        System.out.println("Test_A.staticMethod");
    }
}

1.2.4 接口的多继承

接口完全支持多继承。和类的继承类似,子接口扩展某个父接口,将会获得父接口中所 定义的一切。
接口的多继承代码如下:

package com.txw.test;

interface A {
    void testa();
}
interface B {
    void testb();
}
/**接口可以多继承:接口C继承接口A和B*/
interface C extends A, B {
    void testc();
}
public class Test implements C {
    public void testc() { }
    public void testa() { }
    public void testb() { }
}

1.2.5 面向接口编程

面向接口编程是面向对象编程的一部分。
为什么需要面向接口编程? 软件设计中最难处理的就是需求的复杂变化,需求的变化更 多的体现在具体实现上。我们的编程如果围绕具体实现来展开就会陷入”复杂变化”的汪洋 大海中,软件也就不能最终实现。我们必须围绕某种稳定的东西开展,才能以静制动,实现 规范的高质量的项目。
接口就是规范,就是项目中最稳定的核心! 面向接口编程可以让我们把握住真正核心 的东西,使实现复杂多变的需求成为可能。
通过面向接口编程,而不是面向实现类编程,可以大大降低程序模块间的耦合性,提高 整个系统的可扩展性和和可维护性。
面向接口编程的概念比接口本身的概念要大得多。设计阶段相对比较困难,在你没有写 实现时就要想好接口,接口一变就乱套了,所以设计要比实现难!

建议:
接口语法本身非常简单,但是如何真正使用?这才是大学问。我们需要后面在项目中反复使 用,大家才能体会到。 学到此处,能了解基本概念,熟悉基本语法,就是“好学生”了。 请继 续努力!再请工作后,闲余时间再看看上面这段话,相信你会有更深的体会。

2 字符串 String 类详解

String 是我们开发中最常用的类,我们不仅要掌握 String 类常见的方法,对于 String 的底层实现也需要掌握好,不然在工作开发中很容易犯错。

2.1 String 基础

1.String 类又称作不可变字符序列。
2.String 位于 java.lang 包中,Java 程序默认导入 java.lang 包下的所有类。
3.Java 字符串就是 Unicode 字符序列,例如字符串“Java”就是 4 个 Unicode 字 符’J’、’a’、’v’、’a’组成的。
4.Java 没有内置的字符串类型,而是在标准 Java 类库中提供了一个预定义的类 String,每个用双引号括起来的字符串都是 String 类的一个实例。
String 类的简单使用代码如下:

String e = "" ; // 空字符串 
String greeting = " Hello World ";

5.Java 允许使用符号"+"把两个字符串连接起来。
字符串连接的代码如下:

String s1 = "Hello"; 
String s2 = "World! "; 
String s = s1 + s2; // HelloWorld!

6.符号"+“把两个字符串按给定的顺序连接在一起,并且是完全按照给定的形式。
7.”+"运算符两侧的操作数中只要有一个是字符串(String)类型,系统会自动将另一个操作数转换为字符串然后再进行连接。
"+"连接符的代码如下:

int age = 18; 
String str = "age is" + age; // str赋值为"age is 18" 
// 这种特性通常被用在输出语句中: 
System.out.println("age is" + age);

2.2 String 类和常量池

在 Java 的内存分析中,我们会经常听到关于“常量池”的描述,实际上常量池也分了 以下三种:
1. 全局字符串常量池(String Pool)
全局字符串常量池中存放的内容是在类加载完成后存到 String Pool 中的,在每个 VM 中只有一份,存放的是字符串常量的引用值(在堆中生成字符串对象实例)。
2. class 文件常量池(Class Constant Pool)
class 常量池是在编译的时候每个 class 都有的,在编译阶段,存放的是常量(文本字 符串、final 常量等)和符号引用。
3. 运行时常量池(Runtime Constant Pool)
运行时常量池是在类加载完成之后,将每个 class 常量池中的符号引用值转存到运行时 常量池中,也就是说,每个 class 都有一个运行时常量池,类在解析之后,将符号引用替换 成直接引用,与全局常量池中的引用值保持一致。
字符串相等判断(以后一般判断字符串值是否相等, 使用 equals())的代码如下:

String str1 = "abc"; 
String str2 = new String("abc"); 
String str3 = "abc"; 
System.out.println(str1 == str3); // true 
System.out.println(str2 == str3); // false 
System.out.println(str2.equals(str3)); // true

2.3 阅读 API 文档

· 如何下载 API 文档
1.下载地址,点击进入:

https://www.oracle.com/java/technologies/javase-jdk8-doc-downloads.html

如图所示:在这里插入图片描述
2.下载成功后,解压下载的压缩文件,点击进入 docs/api 下的 index.html 文件即可。如图所示:在这里插入图片描述
·API 文档如何阅读
在这里插入图片描述
· eclipse 中将鼠标放到类或方法上,即可看到相关的注释说明;再按下 F2 即可将 注释窗口固定。在这里插入图片描述
·IDEA 中需要设置才能看相应说明。设置:File–>Setting–>Editor–>General
显示结果如下:

2.4 String 类常用的方法

String 类是我们最常使用的类。字符串类的方法我们必须非常熟悉!我们列出常用的 方法,请大家熟悉。在这里插入图片描述
在这里插入图片描述
String 类常用方法一的代码如下:

package com.txw.test;

public class StringTest1 {
    public static void main(String[ ] args) {
        String s1 = "core Java";
        String s2 = "Core Java";
        System.out.println(s1.charAt(3));	// 提取下标为3的字符
        System.out.println(s2.length());	// 字符串的长度
        System.out.println(s1.equals(s2));	// 比较两个字符串是否相等
        System.out.println(s1.equalsIgnoreCase(s2));	// 比较两个字符串(忽略大小写)
        System.out.println(s1.indexOf("Java"));	// 字符串s1中是否包含Java
        System.out.println(s1.indexOf("apple"));	// 字符串s1中是否包含apple
        String s = s1.replace(' ', '&');	// 将s1中的空格替换成&
        System.out.println("result is :" + s);
    }
}

执行结果如图所示:在这里插入图片描述
String 类常用方法二的代码如下:

package com.txw.test;

public class StringTest2 {
    public static void main(String[ ] args) {
        String s = "";
        String s1 = "How are you?";
        System.out.println(s1.startsWith("How"));	// 是否以How开头
        System.out.println(s1.endsWith("you"));	// 是否以you结尾
        s = s1.substring(4);	// 提取子字符串:从下标为4的开始到字符串结尾为止
        System.out.println(s);
        s = s1.substring(4, 7);	//提取子字符串:下标[4, 7) 不包括7
        System.out.println(s);
        s = s1.toLowerCase();	// 转小写
        System.out.println(s);
        s = s1.toUpperCase();	// 转大写
        System.out.println(s);
        String s2 = " How old are you!! ";
        s = s2.trim();	// 去除字符串首尾的空格。注意:中间的空格不能去除
        System.out.println(s);
        System.out.println(s2);	// 因为String是不可变字符串,所以s2不变
    }
}

执行结果如图所示:在这里插入图片描述

2.5 字符串相等的判断

1.equals 方法用来检测两个字符串内容是否相等。如果字符串 s 和 t 内容相等,则 s.equals(t)返回 true,否则返回 false。
2.要测试两个字符串除了大小写区别外是否是相等的,需要使用 equalsIgnoreCase 方法。
3.判断字符串是否相等不要使用"=="。
忽略大小写的字符串比较的代码如下:

"Hello".equalsIgnoreCase("hellO");		// true

字符串的比较:"= ="与 equals()方法的代码如下:

package com.txw.test;

public class StringTest3 {
    public static void main(String[ ] args) {
        String g1 = "学无止路";
        String g2 = "学无止路";
        String g3 = new String("学无止路");
        System.out.println(g1 == g2); // true 指向同样的字符串常量对象
        System.out.println(g1 == g3); // false g3是新创建的对象
        System.out.println(g1.equals(g3)); // true g1和g3里面的字符串内容是一样的
    }
}

执行结果如图所示:在这里插入图片描述

3 内部类

内部类是一类特殊的类,指的是定义在一个类的内部的类。实际开发中,为了方便的使 用外部类的相关属性和方法,这时候我们通常会定义一个内部类。如图所示:在这里插入图片描述

3.1 内部类的概念

一般情况,我们把类定义成独立的单元。有些情况下,我们把一个类放在另一个类的内 部定义,称为内部类(innerclasses)。
内部类可以使用 public、default、protected 、private 以及 static 修饰。而外部顶 级类(我们以前接触的类)只能使用 public 和 default 修饰。

注意:
内部类只是一个编译时概念,一旦我们编译成功,就会成为完全不同的两个类。对于一个名 为 Outer 的外部类和其内部定义的名为 Inner 的内部类。编译完成后会出现 Outer.classOuter$Inner.class 两个类的字节码文件。所以内部类是相对独立的一种存在,其成员变量/方法 名可以和外部类的相同。

内部类的展示代码如下:

package com.txw.test;

/**外部类 Outer*/
class Outer {
    private int age = 10;
    public void show(){
        System.out.println(age);	// 10
    }
    /**内部类 Inner*/
    public class Inner {
        // 内部类中可以声明与外部类同名的属性与方法 
        private int age = 20;
        public void show(){
            System.out.println(age);	// 20
        }
    }
}

编译后会产生两个不同的字节码文件,如图所示:在这里插入图片描述

内部类的作用:
1.内部类提供了更好的封装。只能让外部类直接访问,不允许同一个包中的其他类直 接访问。 
2.内部类可以直接访问外部类的私有属性,内部类被当成其外部类的成员。 但外部类 不能访问内部类的内部属性。

3.2 内部类的分类

在 Java 中内部类主要分为成员内部类(非静态内部类、静态内部类)、匿名内部类、 局部内部类。在这里插入图片描述

3.2.1 非静态内部类

非静态内部类(外部类里使用非静态内部类和平时使用其他类没什么不同)
1.非静态内部类对象必须寄存在一个外部类对象里。因此,如果有一个非静态内部类 对象那么一定存在对应的外部类对象。非静态内部类对象单独属于外部类的某个对 象。
2.非静态内部类可以直接访问外部类的成员,但是外部类不能直接访问非静态内部类 成员。
3.非静态内部类不能有静态方法、静态属性和静态初始化块。
4.成员变量访问要点
1.内部类里方法的局部变量:变量名。
2.内部类属性:this.变量名。
3.外部类属性:外部类名.this.变量名。
内部类中访问成员变量的代码如下:

package com.txw.test;

/**外部类Outer1*/
class Outer1 {
    private int age = 10;
    public void show(){
        System.out.println(age);	// 10
    }
    /**内部类Inner*/
    public class Inner1 {
        // 内部类中可以声明与外部类同名的属性与方法 
        private int age = 20;
        public void show(){
            System.out.println(age);		// 20
            System.out.println(Outer1.this.age); 	// 10 访问外部类的普通属性
        }
    }
}

内部类的访问
1.外部类中定义内部类:new Inner()。
2.外部类以外的地方使用非静态内部类: Outer.Inner varname = new Outer().new Inner()。
内部类的访问的代码如下:

package com.txw.test;

/**
 *测试非静态内部类 
 */
public class TestInnerClass1 {
    public static void main(String[ ] args) {
// 先创建外部类实例,然后使用该外部类实例创建内部类实例 
        Outer1.Inner1 inner = new Outer1().new Inner1();
        inner.show();
    }
}

3.2.2 静态内部类

定义方式

static class ClassName { 
// 类体
 }

使用要点
1.静态内部类可以访问外部类的静态成员,不能访问外部类的普通成员。
2. 静态内部类看做外部类的一个静态成员。
静态内部类的访问的代码如下:

package com.txw.test;

/*测试静态内部类 */
class Outer2{
    private int a = 10;
    private static int b = 20;
    // 相当于外部类的一个静态成员 
    static class Inner2{
        public void test(){
            // System.out.println(a); // 静态内部类不能访问外部类的普通属性
            System.out.println(b); //静态内部类可以访问外部类的静态 属性
        }
    }

}
public class TestStaticInnerClass {
    public static void main(String[ ] args) {
        // 通过new外部类名.内部类名()来创建内部类对象
        Outer2.Inner2 inner =new Outer2.Inner2(); inner.test();
    }
}

3.2.3 匿名内部类

适合那种只需要使用一次的类。比如:键盘监听操作等等。在安卓开发、awt、swing 开发中常见。
语法

new 父类构造器(实参类表) \实现接口 () {
	// 匿名内部类类体! 
}

匿名内部类的使用代码如下:

package com.txw.test;

/**
 * 测试匿名内部类 
 */
public class TestAnonymousInnerClass {
    public void test1(A a) {
        a.run();
    }
    public static void main(String[] args) {
        TestAnonymousInnerClass tac = new TestAnonymousInnerClass();
        tac.test1(new A() {
            @Override
            public void run() {
                System.out.println("匿名内部类测试! 我是新定义的 第一个匿名内部类!");
            }
        });
        tac.test1(new A() {
            @Override
            public void run() {
                System.out.println("我是新定义的第二个匿名内部类 ");
            }
        });
    }
}
interface A {
    void run();
}
注意:
1.匿名内部类没有访问修饰符。 
2.匿名内部类没有构造方法。因为它连名字都没有那又何来构造方法呢。

3.2.4 局部内部类

定义在方法内部的,作用域只限于本方法,称为局部内部类。
局部内部类的的使用主要是用来解决比较复杂的问题,想创建一个类来辅助我们的解决 方案,到那时又不希望这个类是公共可用的,所以就产生了局部内部类。局部内部类和成员 内部类一样被编译,只是它的作用域发生了改变,它只能在该方法中被使用,出了该方法就 会失效。
局部内部类在实际开发中应用很少。
方法中的内部类代码如下:

package com.txw.test;

/**
 * 测试局部内部类
 */
public class TestLocalInnerClass {
    public void show() {
        // 作用域仅限于该方法
        class Inner3 {
            public void fun() {
                System.out.println("helloworld");
            }
        }
        new Inner3().fun();
    }
    public static void main(String[ ] args) {
        new TestLocalInnerClass().show();
    }
}

执行结果为如图所示:在这里插入图片描述
面向对象大总结(思维导图)在这里插入图片描述

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

学无止路

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值