07:面向对象【4】
目录
一:抽象类和接口
1.1 抽象方法和抽象类
抽象方法 :使用 abstract 修饰的方法,没有方法体,只有声明。定义的是一种“规范”,就是告诉子类必须要给抽象方法提供具体的实现。
抽象类 :包含抽象方法的类就是抽象类。通过abstract 方法定义规范,然后要求子类必须定义 具体实现。通过抽象类,我们就可以做到严格限制子类的设计,使子类之间更加通用。
【示例5-16】抽象类和抽象方法的基本用法
package cn.sxt.text;
//抽象类
abstract class Animalx{
abstract public void shout(); //抽象方法
}
class Dogg extends Animalx{
//子类必须实现父类的抽象方法,否则编译错误
public void shout() {
System.out.println("汪汪汪!!!");
}
public void seeDoor(){
System.out.println("看门中......");
}
}
public class TestAbstractClass {
public static void main(String[] args) {
Dogg a =new Dogg();
a.shout();
a.seeDoor();
Animalx x = new Dogg();
x.shout();
//x.seeDoor(); //编译错误,只能用抽象方法的
}
}
1.2 接口interface
接口就是规范,定义的是一组规则,体现了现实世界中“如果你是…则必须能…”的思 想。如果你是天使,则必须能飞。如果你是汽车,则必须能跑。如果你是好人,则必须能干掉坏人;如果你是坏人,则必须欺负好人。
接口的本质是契约,就像我们人间的法律一样。制定好后大家都遵守。
面向对象的精髓,是对对象的抽象,最能体现这一点的就是接口。为什么我们讨论设计 模式都只针对具备了抽象能力的语言(比如C++、Java、C#等),就是因为设计模式所研究的,实际上就是如何合理的去抽象。
1.2.1 接口的作用
为什么需要接口?接口和抽象类的区别?
接口就是比“抽象类”还“抽象”的“抽象类”,可以更加规范的对子类进行约束。全 面地专业地实现了:规范和具体实现的分离。
抽象类还提供某些具体实现,接口不提供任何实现,接口中所有方法都是抽象方法。接口是完全面向规范的,规定了一批类具有的公共方法规范。
从接口的实现者角度看,接口定义了可以向外部提供的服务。
从接口的调用者角度看,接口定义了实现者能提供那些服务。
接口是两个模块之间通信的标准,通信的规范。如果能把你要设计的模块之间的接口定义好,就相当于完成了系统的设计大纲,剩下的就是添砖加瓦的具体实现了。大家在工作以 后,做系统时往往就是使用“面向接口”的思想来设计系统。
接口和实现类不是父子关系,是实现规则的关系。比如:我定义一个接口 Runnable, Car 实现它就能在地上跑,Train实现它也能在地上跑,飞机实现它也能在地上跑。就是说, 如果它是交通工具,就一定能跑,但是一定要实现 Runnable 接口。
1.2.2 如何定义和使用接口(JDK8以前)
package cn.sxt.text;
import org.w3c.dom.ls.LSOutput;
public class TestInterface {
public static void main(String[] args) {
Volant volant = new Angel();
volant.fly();
System.out.println(volant.FLY_HIGT);
Honest honest = new GoodMan();
honest.helpOther();
}
}
/**飞行接口*/
interface Volant{
int FLY_HIGT = 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 BridMan implements Volant{
public void fly(){
System.out.println("我是鸟人!我在飞!");
}
}
1.2.3 接口中定义静态方法和默认方法(JDK8以后)
JAVA8之前,接口里的方法要求全部是抽象方法。
JAVA8(含 8)之后,以后允许在接口里定义默认方法和类方法。
1. 默认方法
Java8及以上新版本,允许给接口添加一个非抽象的方法实现,只需要使用 default 关 键字即可,这个特征又叫做默认方法(也称为扩展方法)。
默认方法和抽象方法的区别是抽象方法必须要被实现,默认方法不是。作为替代方式,接口可以提供默认方法的实现,所有这个接口的实现类都会通过继承得到这个方法。
本接口的默认方法中可以调用静态方法。
package cn.sxt.text;
public class Test11 {
public static void main(String[] args) {
A a =new B();
a.x();
}
}
interface A{
default void x(){
System.out.println("我是默认方法呀!!!");
}
}
class B implements A{
public void x() {
System.out.println("我不是默认方法呀!!!");
}
}
/**
* 我不是默认方法呀!!!
*/
2. 静态方法
JAVA8以后,我们也可以在接口中直接定义静态方法的实现。这个静态方法直接从属 于接口(接口也是类,一种特殊的类),可以通过接口名调用。
如果子类中定义了相同名字的静态方法,那就是完全不同的方法了,直接从属于子类。 可以通过子类名直接调用。
package cn.sxt.text;
public class Testx {
public static void main(String[] args) {
A.staticMethonA();
B.staticMethonB();
}
}
interface A{
public static void staticMethonA(){
System.out.println("我是A中静态方法!");
}
}
class B implements A{
public static void staticMethonB(){
System.out.println("我是B中的静态方法!");
}
}
/**
* 我是A中静态方法!
* 我是B中的静态方法!
* */
1.2.4 接口的多继承
接口完全支持多继承。和类的继承类似,子接口扩展某个父接口,将会获得父接口中所 定义的一切。
package cn.sxt.text;
interface A{
void testa();
}
interface B{
void testb();
}
/**接口可以多继承:接口 C继承接口 A 和B*/
interface C extends A,B{
void testc();
}
public class Testx implements C {
public void testa(){}
public void testb(){}
public void testc(){}
}
1.2.5 面向接口编程
面向接口编程是面向对象编程的一部分。 为什么需要面向接口编程? 软件设计中最难处理的就是需求的复杂变化,需求的变化更 多的体现在具体实现上。我们的编程如果围绕具体实现来展开就会陷入”复杂变化”的汪洋 大海中,软件也就不能最终实现。我们必须围绕某种稳定的东西开展,才能以静制动,实现 规范的高质量的项目。
接口就是规范,就是项目中最稳定的核心! 面向接口编程可以让我们把握住真正核心 的东西,使实现复杂多变的需求成为可能。
通过面向接口编程,而不是面向实现类编程,可以大大降低程序模块间的耦合性,提高 整个系统的可扩展性和和可维护性。
面向接口编程的概念比接口本身的概念要大得多。设计阶段相对比较困难,在你没有写 实现时就要想好接口,接口一变就乱套了,所以设计要比实现难!
二:字符串String类详解
2.1 String基础
- String类又称作不可变字符序列。
- String位于 java.lang 包中,Java程序默认导入 java.lang 包下的所有类。
- Java字符串就是 Unicode 字符序列,例如字符串“Java”就是4个 Unicode 字 符’J’、’a’、’v’、’a’组成的。
- Java 没有内置的字符串类型,而是在标准 Java 类库中提供了一个预定义的类 String,每个用双引号括起来的字符串都是String 类的一个实例
【示例5-25】String类的简单使用
String e = "" ; // 空字符串
String greeting = " Hello World "
2.2 String类和常量池
在Java的内存分析中,我们会经常听到关于“常量池”的描述,实际上常量池也分了 以下三种:
1. 全局字符串常量池(String Pool)
全局字符串常量池中存放的内容是在类加载完成后存到StringPool 中的,在每个 VM 中只有一份,存放的是字符串常量的引用值(在堆中生成字符串对象实例)。
2. class 文件常量池(Class Constant Pool)
class 常量池是在编译的时候每个 class 都有的,在编译阶段,存放的是常量(文本字 符串、final 常量等)和符号引用。
3. 运行时常量池(Runtime Constant Pool)
运行时常量池是在类加载完成之后,将每个 class 常量池中的符号引用值转存到运行时 常量池中,也就是说,每个class都有一个运行时常量池,类在解析之后,将符号引用替换 成直接引用,与全局常量池中的引用值保持一致。
【示例5-28】字符串相等判断(以后一般判断字符串值是否相等,使用equals())
package cn.sxt.text;
public class Testx {
public static void main(String[] args) {
String str1 ="abc";
String str2 = new String("abc");
String str3 = "abc";
System.out.println(str1==str3);
System.out.println(str2==str3);
System.out.println(str2.equals(str3));
}
}
/***
* true
* false
* true
*/
2.3 String类常用的方法
package com.bjsxt.string;
public class Test1 {
public static void main(String[] args) {
String str1 = new String("abcdefg");
String str2 = "abcdefg";
String str3 = "abcdEFG";
String str4 = "def";
System.out.println(str1==str2);
System.out.println(str1);
System.out.println(str2);
//设计到字符串比较的时候,都用equals方法
System.out.println(str1.equals(str2));
System.out.println(str1.length()); //7
System.out.println(str2.charAt(0));
System.out.println(str2.charAt(6)); //str2.length()-1
System.out.println(str2.charAt(str2.length()-1)); //取字符串的最后一个字符
System.out.println(str2.equals(str3)); //false
System.out.println(str2.equalsIgnoreCase(str3)); //true
//从开头到结尾查找,返回找到的第一个子字符串的索引位置。如未找到,返回-1
System.out.println("abcdefgdefg".indexOf("def")); //结果:3
System.out.println("abcdefgdefg".indexOf("DF")); //结果:-1
//从末尾开始查找
System.out.println("abcdefgdefg".lastIndexOf("def")); //结果:7
//字符串的替换
String str5 = "abcdbcd".replace('d','D');
System.out.println(str5); //abcDbcD
String str6 = "abcdbcd".replace("cd","HELLO");
System.out.println(str6); //abHELLObHELLO
System.out.println("sxt,i love u".startsWith("sxt")); //true
System.out.println("sxt,i love u".endsWith("sxt")); //false
//截取子字符串
String str7 = "abcdefghijklmnopqrstuvwxyz".substring(6);
System.out.println(str7); //ghijklmnopqrstuvwxyz
String str8 = "abcdefghijklmnopqrstuvwxyz".substring(6,11); //6-(11-1)
System.out.println(str8); //ghijk
System.out.println("abcdE".toUpperCase());
System.out.println("abcdE".toLowerCase());
String str9 = " a b "; //长度7
String str10 = str9.trim();
System.out.println(str10.length());
System.out.println(str10);
//String是不可变字符序列。所有的替换、截取子字符串、去空格、转换大小写等都是生成了新字符串
System.out.println(str9.replace(" ",""));
System.out.println(str9);
}
}
2.4 字符串相等的判断
- equals 方法用来检测两个字符串内容是否相等。如果字符串 s 和 t 内容相等,则 s.equals(t)返回 true,否则返回 false。
- 要测试两个字符串除了大小写区别外是否是相等的,需要使用equalsIgnoreCase 方法。
- 判断字符串是否相等不要使用"=="。
【示例5-32】字符串的比较:"= ="与equals()方法
package cn.sxt.text;
import org.w3c.dom.ls.LSOutput;
public class name {
public static void main(String[] args) {
String g1 ="sshyes";
String g2 ="sshyes";
String g3 =new String("sshyes");
System.out.println(g1==g2);
System.out.println(g1==g3);
System.out.println(g1.equals(g3));
}
}
/**
* true
* false
* true
*/
三:内部类
3.1 内部类
内部类是一类特殊的类,指的是定义在一个类的内部的类。实际开发中,为了方便的使 用外部类的相关属性和方法,这时候我们通常会定义一个内部类。
3.2 内部类的概念
一般情况,我们把类定义成独立的单元。有些情况下,我们把一个类放在另一个类的内 部定义,称为内部类(innerclasses)。 内部类可以使用 public、default、protected 、private 以及 static 修饰。而外部顶 级类(我们以前接触的类)只能使用 public和default 修饰。
注意 内部类只是一个编译时概念,一旦我们编译成功,就会成为完全不同的两个类。对于一个名 为 Outer 的外部类和其内部定义的名为 Inner 的内部类。编译完成后会出现 Outer.class 和 Outer$Inner.class两个类的字节码文件。所以内部类是相对独立的一种存在,其成员变量/方法 名可以和外部类的相同。
package mycode;
import org.w3c.dom.ls.LSOutput;
/**外部类Outer*/
public class Outer {
private int age =10;
public void show(){
System.out.println(age);
}
/**外部类*/
public class Inner{
//内部类中可以声明与外部类同名的属性与方法
private int age = 20;
public void show(){
System.out.println(age);
/**访问外部类的变量*/
System.out.println(Outer.this.age);
}
}
public static void main(String[] args) {
Outer.Inner o =new Outer().new Inner();
System.out.println(o.age);
o.show();
}
}
/**
* 20
* 20
* 10
*/
内部类的作用:
- 内部类提供了更好的封装。只能让外部类直接访问,不允许同一个包中的其他类直接访问。
- 内部类可以直接访问外部类的私有属性,内部类被当成其外部类的成员。 但外部类 不能访问内部类的内部属性。
3.3 内部类的分类
在Java中内部类主要分为成员内部类(非静态内部类、静态内部类)、匿名内部类、 局部内部类。
非静态内部类
非静态内部类(外部类里使用非静态内部类和平时使用其他类没什么不同)
- 非静态内部类对象必须寄存在一个外部类对象里。因此,如果有一个非静态内部类 对象那么一定存在对应的外部类对象。非静态内部类对象单独属于外部类的某个对 象。
- 非静态内部类可以直接访问外部类的成员,但是外部类不能直接访问非静态内部类 成员。
- 非静态内部类不能有静态方法、静态属性和静态初始化块。
- 成员变量访问要点:
- 内部类里方法的局部变量:变量名。
- 内部类属性:this.变量名。
- 外部类属性:外部类名.this.变量名
内部类的访问:
1. 外部类中定义内部类:new Inner()。 2. 外部类以外的地方使用非静态内部类: Outer.Inner varname = new Outer().new Inner()。
静态内部类
package mycode;
/*
测试静态内部类
*/
public 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);
}
}
}
class Outerx{
public static void main(String[] args) {
Outer2.Inner2 inner =new Outer2.Inner2();
inner.test();
}
}
/**
* 20
*/
匿名内部类
package mycode;
public class Testx {
public void test1(A a){
a.run();
}
public static void main(String[] args) {
Testx t =new Testx();
t.test1(new A() {
@Override
public void run() {
System.out.println("第一个内部类");
}
});
}
}
interface A{
void run();
}
注意
- 匿名内部类没有访问修饰符。
- 匿名内部类没有构造方法。因为它连名字都没有那又何来构造方法呢。
局部内部类
定义在方法内部的,作用域只限于本方法,称为局部内部类。
局部内部类的的使用主要是用来解决比较复杂的问题,想创建一个类来辅助我们的解决 方案,到那时又不希望这个类是公共可用的,所以就产生了局部内部类。局部内部类和成员 内部类一样被编译,只是它的作用域发生了改变,它只能在该方法中被使用,出了该方法就 会失效。
局部内部类在实际开发中应用很少。
本文地址:https://blog.csdn.net/weixin_44359595/article/details/107666515
上一篇: 面向对象的三大特征和基本语法
下一篇: 面向对象编程--1、类和对象