Java学习笔记 -- 第九章 面向对象(四):特殊的类

写在开头:今天我们来学习面向对象的第四部分:面向对象的一些特殊的类,包括抽象类,接口还有内部类。
PS:本文是个人学习笔记,用于个人复习。文章内容引用尚学堂java300集教学。

特殊的类

    Ⅰ 抽象类
    Ⅱ 接口
    Ⅲ String类
    Ⅳ 内部类

Ⅰ 抽象类

  1. 抽象方法
      使用abstract修饰的方法,没有方法体,只有声明。定义的是一种“规范”,就是告诉子类必须要给抽象方法提供具体的实现。

  2. 抽象类
      包含抽象方法的类就是抽象类。通过abstract方法定义规范,然后要求子类必须定义具体实现。通过抽象类,我们就可以做到严格限制子类的设计,便于子类之间更加通用。

  3. 抽象类要点
      - 有抽象方法的类只能定义成抽象类
      - 抽象类不能实例化,即不能用new来实例化抽象类
      - 抽象类可以包含属性、方法、构造方法。但是构造方法不能用来new实例,只能用来被子类调用
      - 抽象类只能用来被继承
      - 抽象方法必须被子类实现

    举个例子:

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

Ⅱ 接口

  1. 接口的概念
      接口就是规范,定义的是一组规则,体现了现实世界中,“如果你是…则必须能…”的思想。
      接口的本质是契约,就像法律一样,制定好后大家都遵守。
      面向对象的精髓,是对对象的抽象,最能体现这一点的就是接口。所谓的设计模式,其研究的就是如何合理的去抽象。

  2. 接口的作用(为什么需要接口?接口和抽象类有什么区别?)
      接口就是比“抽象类”还“抽象”的“抽象类”可以更加规范的对子类进行约束。全面专业地实现了:规范和具体实现的分离。
      抽象类还提供某些具体的实现,接口不提供任何实现。接口中的所有方法都是抽象方法。接口是完全面向规范的,规定了一批类具有的公共方法规范。
      从接口的实现者角度来看,接口定义了可以向外部提供的服务。
      从接口的调用者来看,接口定义了实现者能提供哪些服务。
      接口是两个模块之间通信的标准,通信的规范。
      接口和实现类不是父子关系,是实现规则的关系。

      接口、抽象类和普通类的区别:
        - 普通类:具体实现
        - 抽象类:具体实现,规范(抽象方法)
        - 接口:规范

  3. 接口的定义和使用
      声明格式:

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

      接口的使用:

    [修饰符] class类名 [extends 父类] implements 接口名{
    	@override
    	重写接口方法;
    }
    

    举个简单的例子:

    //接口的声明
    public interface AD {
    	 public void physicAttack();
    }
    
    //接口的调用
    public class ADHero implements AD{
    	 public void physicAttack(){
    	 	System.out.println("进行物理攻击");
    	 }
    }
    

    定义接口的详细说明:
      - 访问修饰符:只能是public或者默认
      - 接口名:和类名采用相同的命名机制
      - extends:接口可以多继承
      - 常量:接口中的属性只能是常量,总是:public static final 修饰
      - 方法:接口中的方法只能是:public abstract

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

  4. 接口中定义静态方法和默认方法(jdk8以后)
      - 默认方法
        Java8以上的版本,允许给接口添加一个非抽象的方法实现,只需要default关键字即可,这个特征又叫默认方法(也成为扩展方法)。
        默认方法和抽象方法的区别是抽象方法必须要被实现,默认方法不是。作为替代方式,接口可以提供默认方法的实现,所有这个接口的实现类都会通过继承得到这个方法。
      - 静态方法
        Java8以后,我们也可以在接口中直接定义静态方法的实现。这个静态方法直接从属于接口,可以通过接口名调用。
        如果子类中定义了相同名字的静态方法,那就是完全不同的方法了,直接从属于子类。
        值得一提的是,本接口的默认方法中可以调用静态方法。

  5. 接口多继承
      接口完全支持多继承。和类的继承相似,子接口扩展某个父接口,将会获得父接口中所定义的一切。
    示例:

    //定义物理攻击接口
    public interface AD {
    	public void physicAttack();
    }
    //定义魔法攻击接口
    public interface AP {
    	public void magicAttack();
    }
    //定义行为接口,并继承物理攻击接口与魔法攻击接口
    public interface Action extends AD,AP {
    	public void heal();
    }
    
    //创建英雄类,并实现行为接口
    public class Hero implements Action{
    	public void physicAttack(){
    		System.out.println("进行物理攻击");
    	}
    	public void magicAttack(){
    		System.out.println("进行魔法攻击");
    	}
    	public void heal(){
    		System.out.println("进行治疗");
    	}
    }
    

Ⅲ String类

  1. String基础
    (1)基本知识
      - String类又称作不可变字符序列
      - String位于java.lang包中,java程序默认导入java.lang包下的所有类
      - Java字符串是Unicode字符序列
      - Java没有内置的字符串类型,而是在标准Java库中提供了一个预定义的类String,每个用双引号括起来的字符串都是String类的一个实例

    (2)简单使用

    public class TestString {
    	public static void main(String[] args) {
    		String s1 = new String("abc");  //创建字符穿s1
    		String s2 = "def";  //创建字符穿s2
    
    		String s3 = s1 + s2;    //通过 + 对字符穿s1和s2进行拼接
    
    		System.out.println("s1= " + s1);
    		System.out.println("s2= " + s2);
    		System.out.println("s3= " + s3);
    	}
    }
    
  2. String类和常量池
      - 全局常量池
        局常量池中存放的内容是在类加载完成后到String Pool中的,在每个JVM中只有一份,存放的是字符串常量的引用值(在堆中生成字符串对象实例)
      - Class文件常量池
        Class常量池是在编译的时候每个class都有的,在编译阶段,存放的是常量(文本字符串、final常量等)和符号引用
      - 运行时常量池
        运行时常量池是在类加载完成之后,将每个class常量池中的符号引用值转存到运行时常量池中,也就是说,每个class都有一个运行时常量池,类在解析之后,将符号引用替换成直接引用,与全局常量池中的引用保持一致。

  3. String类的常用方法

    方法 解释说明
    char charArt(int index) 返回字符串中第index个字符
    boolean equals(String other) 如果字符串与other相等,返回true;否则,返回false
    boolean equalsIgnoreCase(String other) 如果字符串与other相等(忽略大小写),返回true;否则,返回false
    int indexOf(String str) 返回从头开始查找第一个字符串str在字符串中的索引位置。如果未找到子字符串str,则返回-1
    lastIndexOf() 返回从末尾开始查找第一个子字符串str在字符串中的索引位置。如果未找到子字符串str,则返回-1
    int length() 返回字符串的长度
    String replace(char oldChar, char newChar) 返回一个新串,它是通过用 newChar 替换此字符串中出现的所有 oldChar 而生成的
    boolean srartsWith(String prefix) 如果字符串以prefix开始,则返回 true
    boolean endsWith(String prefix) 如果字符串以prefix结尾,则返回 true
    String substring(int beginIndex) 返回一个新字符串,该串包含从原始字符串 beginIndex 到串尾
    String substring(int beginIndex, int endInedex) 返回一个新字符串,该串包含从原始字符串 beginIndex 到串尾或 endIndex-1 的所有字符
    String toLowerCase() 返回一个新字符串,该串将原始字符串中的所有大写字母改成小写字母
    String toUpperCase() 回一个新字符串,该串将原始字符串中的所有小写字母改成大写字母
    String trim() 返回一个新字符串,该串删除了原始字符串头部和尾部的空格

    举例:

    public class TestString {
    	public static void main(String[] args) {
    		String s1 = "abcdefg";
    		String s2 = "ABCDEFG";
    		String s3 = "  abc  def g  ";
    
    		System.out.println(s1.length());                //获取字符穿长度
    		
    		System.out.println(s1.charAt(2));               //返回字符串第3个字符
    
    		System.out.println(s1.charAt(s1.length() - 1)); //返回字符串最后一个字符
    
    		System.out.println(s1.equals(s2));              //比较两个字符串的值(区分大小写)
    
    		System.out.println(s1.equalsIgnoreCase(s2));    //比较两个字符串的值(忽略大小写)
    
    		//从开头开始查找,返回找到的第一个子字符串的索引位置。如未找到,返回-1
    		System.out.println(s1.indexOf("de"));
    		//从末尾开始查找,返回找到的第一个字符串的索引位置。如未找到,返回-1
    		System.out.println(s1.lastIndexOf("bc"));
    
    		//字符串的替换(可单个字符,也可字符串)
    		System.out.println(s1.replace("a","A"));
    		System.out.println(s1.replace("abc","ABC"));
    
    		//判断是否以某个字符串开头
    		System.out.println(s1.startsWith("abc"));
    		//判断是否以某个字符串结尾
    		System.out.println(s1.endsWith("abc"));
    
    		//截取子字符串
    		System.out.println(s1.substring(3));
    		System.out.println(s1.substring(3,5));  //左闭右开
    
    		//返回大写/小写字符串
    		System.out.println(s1.toUpperCase());	//返回大写
    		System.out.println(s2.toLowerCase());	//返回小写
    
    		//删除头尾空格
    		System.out.println(s3.trim());
    	}
    }
    

    运行结果:

Ⅳ 内部类

  1. 内部类的概念
      一般情况,我们把类定义成独立的单元。有些情况下,我们把一个类放在另一个类的内部定义,成为内部类。
      内部类可以使用public、default、protected、private和static修饰。而外部顶级类只能使用public和default修饰。

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

    //外部类
    public class Face {
    	private String shape = "圆形";
    	public void takePhoto(){
    		System.out.println("拍了张自拍~");
    	}
    
    	//内部类
    	private class Nose{
    		private String shape = "高鼻梁";
    		public void breathe() {
    			takePhoto();    //内部类可以直接使用外部类的成员
    			
    			//当外部类属性和内部类属性发生重名时,可以通过:Outer.this.成员名
    			System.out.println(shape + "的鼻子长在了" + Face.this.shape + "的脸上");
    			System.out.println("呼吸中~");			
    		}
    	}
    	
    	//main函数
    	public static void main(String[] args) {
    		Face.Nose n = new Face().new Nose();
    		n.breathe();
    	}
    }
    

    运行结果:

  3. 内部类的分类
      在java中内部类主要分为成员内部类(非静态内部类、静态内部类)、匿名内部类、局部内部类

      (1)非静态内部类
        - 非静态内部类必须寄存在一个外部类杜象里。因此,如果有一个非静态内部类对象那么一定存在对应的外部类对象。非静态内部类对象单独属于外部类的某个对象
        - 非静态内部类可以直接访问外部类的成员,但是外部类不能直接访问非静态内部类成员
        - 非静态方法不能有静态方法、静态属性和静态初始化块
        - 成员变量访问要点:
          + 内部类里方法的局部变量:变量名
          + 内部类属性:this.变量名
          + 外部类属性:外部类名.this.变量名

      (2)静态内部类
        - 定义方式:

    static class ClassName{
    	//类体
    }
    

        - 使用要点:
          + 静态内部类可以访问外部类的静态成员,不能访问外部类的普通成员
          + 静态内部类看作外部类的一个静态成员

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

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

    举例:

    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();
    }
    

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

发布了11 篇原创文章 · 获赞 3 · 访问量 906

猜你喜欢

转载自blog.csdn.net/IceTiger_/article/details/105403511