【Java基础】

本文详细介绍了Java的面向对象特性,包括访问权限、内部类、方法重载、继承、多态、抽象类和接口。重点讨论了继承的构造方法、抽象方法、接口的静态字段以及多态中的重写和重载。此外,还提到了静态字段和方法、包的作用域以及匿名内部类的概念。最后,简要概述了泛型在Java中的应用。
摘要由CSDN通过智能技术生成


1.面向对象

1.0 访问权限

1.0.1 类的访问权限

默认
这意味着当前包中的所有其他类都可以访问那个成员。对于这个包之外的类,这个成员看上去是 private 的。

public:

  • 加public表示全局类,该类可以import到任何类内去创建、访问。
  • 不加public默认为保留类,只能被同一个包内的其他类引用。

1.0.2 字段&方法的访问权限

  • private:
    -定义private方法的理由是内部方法是可以调用private方法的,但是外部不能
    为了避免外部代码直接去访问field,我们可以用private修饰field,拒绝外部访问
    例【编译报错】:
public class Main {
    public static void main(String[] args) {
        Person ming = new Person();
        ming.name = "Xiao Ming"; // 对字段name赋值
        ming.age = 12; // 对字段age赋值
    }
}

class Person {
    private String name;
    private int age;
}

1.1 一个public类

  • 一个Java源文件可以包含多个类的定义,但只能定义一个public类,且public类名必须与文件名一致。(如果要定义多个public类,必须拆到多个Java源文件中)
  • 加public表示全局类,该类可以import到任何类内。不加public默认为保留类,只能被同一个包内的其他类引用。

1.2 内部类

  • 内部类与外部类可以方便的访问彼此的私有域(包括私有方法、私有属性)。
  • 在外部类内部创建内部类语法
    在外部类内部创建内部类,就像普通对象一样直接创建:Inner in = new Inner();

1.2.1 内部类分类

成员内部类
成员内部类内部不允许存在任何static变量或方法 正如成员方法中不能有任何静态属性 (成员方法与对象相关、静态属性与类有关)

class Outer {
    private String name = "test";
    public  static int age =20;

    class Inner{
        public static int num =10;
        public void fun()
        {
            System.out.println(name);
            System.out.println(age);
        }
    }
}
public class Test{
    public static void main(String [] args)
    {}
}

匿名内部类

  • 必须继承自抽象类或者接口
  • 匿名类和局部内部类一样,可以访问外部类的所有成员。
  • 实现方法

1.3 方法重载 Overload

  • 重载(overloading) 是在一个类里面,方法名字相同,而参数不同。返回类型可以相同也可以不同。

  • 每个重载的方法(或者构造函数)都必须有一个独一无二的参数类型列表。

最常用的地方就是构造器的重载。

1.4 继承

  • Java使用extends关键字来实现继承
  • 不需要重复相同字段和方法,只需新增,子类自动获得了父类的所有字段,严禁定义与父类重名的字段!
  • Java只允许一个class继承自一个类,因此,一个类有且仅有一个父类。只有Object特殊,它没有父类
  • 子类无法访问父类的private字段或者private方法
  • protected修饰的字段可以被子类访问

1.4.1 关于继承的构造方法

  • 在Java中,任何class的构造方法,第一行语句必须是调用父类的构造方法
  • 如果父类没有默认的构造方法,子类就必须显式调用super()并给出参数以便让编译器定位到父类的一个合适的构造方法。
  • 子类不会继承父类的构造方法

1.4.2 阻止继承

  • 只要某个class没有final修饰符,那么任何类都可以从该class继承。
  • 允许使用sealed修饰class,并通过permits明确写出能够从该class继承的子类名称。
    例:定义一个Shape类:
public sealed class Shape permits Rect, Circle, Triangle 
{
    ...
}

1.4.3 向上转型

如果Student是从Person继承下来的,那么,一个引用类型为Person的变量,能否指向Student类型的实例?

Person p = new Student();

向上转型实际上是把一个子类型安全地变为更加抽象的父类型:
【例】

class Animal{
   public void move(){
      System.out.println("动物可以移动");
   }
}
 
class Dog extends Animal{
   public void move(){
      System.out.println("狗可以跑和走");
   }
}
 
public class TestDog{
   public static void main(String args[]){
      Animal a = new Animal(); // Animal 对象
      Animal b = new Dog(); // Dog 对象
 
      a.move();// 执行 Animal 类的方法
 
      b.move();//执行 Dog 类的方法
   }
}

尽管 b 属于 Animal 类型,但是它运行的是 Dog 类的 move方法。

这是由于在编译阶段,只是检查参数的引用类型。

然而在运行时,Java 虚拟机(JVM)指定对象的类型并且运行该对象的方法。

因此在上面的例子中,之所以能编译成功,是因为 Animal 类中存在 move 方法,然而运行时,运行的是特定对象的方法。

思考以下例子:

1.4.4 向下转型

利用instanceof,在向下转型前可以先判断:

Person p = new Student();
if (p instanceof Student) {
    // 只有判断成功才会向下转型:
    Student s = (Student) p; // 一定会成功
}

1.5 多态

1.5.1【重写】Override

在继承关系中,子类如果定义了一个与父类方法参数列表、返回值、方法名完全相同的方法,被称为覆写(Override)。

  • 返回值和形参都不能改变
  • 子类和父类在同一个包中,那么子类可以重写父类所有方法,除了声明为 private 和 final 的方法。
  • 子类和父类不在同一个包中,那么子类只能够重写父类的声明为 public 和 protected 的非 final 方法。
  • 当需要在子类中调用父类的被重写方法时,要使用 super 关键字。
class Dog extends Animal{
   public void move(){
      super.move(); // 应用super类的方法
      System.out.println("狗可以跑和走");
   }
} 

1.5.2 重写与重载之间的区别

在这里插入图片描述

1.5.3 多态

  • 多态是指,针对某个类型的方法调用,其真正执行的方法取决于运行时期实际类型的方法
  • 子类的覆写方法中,如果要调用父类的被覆写的方法,可以通过super来调用。例如:
  • 如果一个父类不允许子类对它的某个方法进行覆写,可以把该方法标记为final。用final修饰的方法不能被Override
  • 用final修饰的类不能被继承
  • 对于一个类的实例字段,同样可以用final修饰。用final修饰的字段在初始化后不能被修改。在构造方法中初始化final字段,实例一旦创建,其final字段就不可修改。

1.6 抽象类

如果父类的方法本身不需要实现任何功能,仅仅是为了定义方法签名,目的是让子类去覆写它,那么,可以把父类的方法声明为抽象方法:

class Person {
    public abstract void run();
}
  • 必须把Person类本身也声明为abstract,才能正确编译它
  • 我们无法实例化一个抽象类
  • 因为抽象类本身被设计成只能用于被继承,因此,抽象类可以强迫子类实现其定义的抽象方法,否则编译会报错。因此,抽象方法实际上相当于定义了“规范”。

1.7 接口

如果一个抽象类没有字段,所有方法全部都是抽象方法,就可以把该抽象类改写为接口:interface。在Java中,使用interface可以声明一个接口:

interface Person {
    void run();
    String getName();
}
  • 所谓interface,就是比抽象类还要抽象的纯抽象接口,因为它连字段都不能有。因为接口定义的所有方法默认都是public abstract的。
  • 当一个具体的class去实现一个interface时,需要使用implements关键字
class Student implements Person {
    private String name;

    public Student(String name) {
        this.name = name;
    }

    @Override
    public void run() {
        System.out.println(this.name + " run");
    }

    @Override
    public String getName() {
        return this.name;
    }
}

在Java中,一个类只能继承自另一个类,不能从多个类继承。
但是,一个类可以实现多个interface,例如

class Student implements Person, Hello { // 实现了两个interface
   ...
}

在使用的时候,实例化的对象永远只能是某个具体的子类,但总是通过接口去引用它,因为接口比抽象类更抽象:

List list = new ArrayList(); // 用List接口引用具体子类的实例
Collection coll = list; // 向上转型为Collection接口
Iterable it = coll; // 向上转型为Iterable接口

1.7.1 接口继承

一个interface可以继承自另一个interface。interface继承自interface使用extends,它相当于扩展了接口的方法。例如:

interface Hello {
    void hello();
}

interface Person extends Hello {
    void run();
    String getName();
}

1.7.2 default方法

在接口中,可以定义default方法。例如,把Person接口的run()方法改为default方法:

public class Main {
    public static void main(String[] args) {
        Person p = new Student("Xiao Ming");
        p.run();
    }
}

interface Person {
    String getName();
    default void run() {
        System.out.println(getName() + " run");
    }
}

class Student implements Person {
    private String name;

    public Student(String name) {
        this.name = name;
    }

    public String getName() {
        return this.name;
    }
}


实现类可以不必覆写default方法。default方法的目的是,当我们需要给接口新增一个方法时,会涉及到修改全部子类。如果新增的是default方法,那么子类就不必全部修改,只需要在需要覆写的地方去覆写新增方法。

1.8 静态字段和静态方法

1.8.1 静态字段

  • 用static修饰的字段,称为静态字段:static field
  • 实例字段在每个实例中都有自己的一个独立“空间”,但是静态字段只有一个共享“空间”,所有实例都会共享该字段。对于静态字段,无论修改哪个实例的静态字段,效果都是一样的:所有实例的静态字段都被修改了
public class Main {
    public static void main(String[] args) {
        Person ming = new Person("Xiao Ming", 12);
        Person hong = new Person("Xiao Hong", 15);
        ming.number = 88;
        System.out.println(hong.number);
        hong.number = 99;
        System.out.println(ming.number);
    }
}

class Person {
    public String name;
    public int age;

    public static int number;

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }
}

在这里插入图片描述

1.8.2 静态方法

  • 用static修饰的方法称为静态方法。

  • 调用实例方法必须通过一个实例变量,而调用静态方法则不需要实例变量,通过类名就可以调用静态方法类似其它编程语言的函数

  • 因为静态方法属于class而不属于实例,因此,静态方法内部,无法访问this变量,也无法访问实例字段,它只能访问静态字段

1.8.3 接口的静态字段

因为interface是一个纯抽象类,所以它不能定义实例字段。但是,interface是可以有静态字段的,并且静态字段必须为final类型:

public interface Person {
    public static final int MALE = 1;
    public static final int FEMALE = 2;
}

1.9 包

  • Java定义了一种名字空间,称之为包:package。一个类总是属于某个包,类名(比如Person)只是一个简写,真正的完整类名是包名.类名
  • 在Java虚拟机执行的时候,JVM只看完整类名,因此,只要包名不同,类就不同。
  • 包没有父子关系

1.9.1 包的作用域

  • 位于同一个包的类,可以访问包作用域的字段和方法。不用public、protected、private修饰的字段和方法就是包作用域。例如,Person类定义在hello包下面

【 import】
在一个class中,我们总会引用其他的class
用import语句,导入小军的Arrays,然后写简单类名:

// Person.java
package ming;

// 导入完整类名:
import mr.jun.Arrays;

public class Person {
    public void run() {
        Arrays arrays = new Arrays();
    }
}

在写import的时候,可以使用*,表示把这个包下面的所有class都导入进来(但不包括子包的class

1.10 作用域总结

1.10.1 public

  • 定义为public的class、interface可以被其他任何类访问
  • 定义为public的field、method可以被其他类访问,前提是首先有访问class的权限
package abc;

public class Hello {
    public void hi() {
    }
}

上面的hi()方法是public,可以被其他类调用,前提是首先要能访问Hello类:

package xyz;

class Main {
    void foo() {
        Hello h = new Hello();
        h.hi();
    }
}

1.10.2 private

  • 定义为private的field、method无法被其他类访问,private访问权限被限定在class的内部
  • 由于Java支持嵌套类,如果一个类内部还定义了嵌套类,那么,嵌套类拥有访问private的权限

1.10.3 protected

protected作用于继承关系。定义为protected的字段和方法可以被子类访问,以及子类的子类:

1.10.4 package作用域

最后,包作用域是指一个类允许访问同一个package的没有public、private修饰的class,以及没有public、protected、private修饰的字段和方法。

1.10.5 final

Java还提供了一个final修饰符。final与访问权限不冲突,它有很多作用。

  • 用final修饰class可以阻止被继承:
package abc;

// 无法被继承:
public final class Hello {
    private int n = 0;
    protected void hi(int t) {
        long i = t;
    }
}
  • 用final修饰method可以阻止被子类覆写:
package abc;

public class Hello {
    // 无法被覆写:
    protected final void hi() {
    }
}
  • 用final修饰field可以阻止被重新赋值:
package abc;

public class Hello {
    private final int n = 0;
    protected void hi() {
        this.n = 1; // error!
    }
}
  • 用final修饰局部变量可以阻止被重新赋值:
package abc;

public class Hello {
    protected void hi(final int t) {
        t = 1; // error!
    }
}

1.10.6 Others

如果不确定是否需要public,就不声明为public,即尽可能少地暴露对外的字段和方法。

把方法定义为package权限有助于测试,因为测试类和被测试类只要位于同一个package,测试代码就可以访问被测试类的package权限方法。

一个.java文件只能包含一个public类,但可以包含多个非public类。如果有public类,文件名必须和public类的名字相同

1.11 内部类

Inner Class的实例不能单独存在,必须依附于一个Outer Class的实例。示例代码如下:

public class Main {
    public static void main(String[] args) {
        Outer outer = new Outer("Nested"); // 实例化一个Outer
        Outer.Inner inner = outer.new Inner(); // 实例化一个Inner
        inner.hello();
    }
}

class Outer {
    private String name;

    Outer(String name) {
        this.name = name;
    }

    class Inner {
        void hello() {
            System.out.println("Hello, " + Outer.this.name);
        }
    }
}

1.11.1 匿名类 Anonymous Class

还有一种定义Inner Class的方法,它不需要在Outer Class中明确地定义这个Class,而是在方法内部,通过匿名类(Anonymous Class)来定义。
示例代码如下
【例】

public class Main {
    public static void main(String[] args) {
        Outer outer = new Outer("Nested");
        outer.asyncHello();
    }
}

class Outer {
    private String name;

    Outer(String name) {
        this.name = name;
    }

    void asyncHello() {
        Runnable r = new Runnable() {
            @Override
            public void run() {
                System.out.println("Hello, " + Outer.this.name);
            }
        };
        new Thread(r).start();
    }
}

观察asyncHello()方法,我们在方法内部实例化了一个Runnable。Runnable本身是接口,接口是不能实例化的,所以这里实际上是定义了一个实现了Runnable接口的匿名类,并且通过new实例化该匿名类,然后转型为Runnable。
在定义匿名类的时候就必须实例化它,定义匿名类的写法如下:

Runnable r = new Runnable() {
    // 实现必要的抽象方法...
};

1.11.2匿名类分类 link

  • 匿名类继承一个父类
    以下实例中,创建了 Polygon 类,该类只有一个方法 display(),AnonymousDemo 类继承了 Polygon 类并重写了 Polygon 类的 display() 方法:
实例
class Polygon {
   public void display() {
      System.out.println("在 Polygon 类内部");
   }
}

class AnonymousDemo {
   public void createClass() {

      // 创建的匿名类继承了 Polygon 类
      Polygon p1 = new Polygon() {
         public void display() {
            System.out.println("在匿名类内部。");
         }
      };
      p1.display();
   }
}

class Main {
   public static void main(String[] args) {
       AnonymousDemo an = new AnonymousDemo();
       an.createClass();
   }
}

执行以上代码,匿名类的对象 p1 会被创建,该对象会调用匿名类的 display() 方法,输出结果为:

在匿名类内部。
  • 匿名类实现一个接口
    以下实例创建的匿名类实现了 Polygon 接口:
    实例
interface Polygon {
   public void display();
}

class AnonymousDemo {
   public void createClass() {

      // 匿名类实现一个接口
      Polygon p1 = new Polygon() {
         public void display() {
            System.out.println("在匿名类内部。");
         }
      };
      p1.display();
   }
}

class Main {
   public static void main(String[] args) {
      AnonymousDemo an = new AnonymousDemo();
      an.createClass();
   }
}

输出结果为:

在匿名类内部。

1.12 泛型

泛型类型用于类的定义中,被称为泛型类。通过泛型可以完成对一组类的操作对外开放相同的接口。最典型的就是各种容器类,如:List、Set、Map。

泛型类的最基本写法(这么看可能会有点晕,会在下面的例子中详解):

//此处T可以随便写为任意标识,常见的如T、E、K、V等形式的参数常用于表示泛型
//在实例化泛型类时,必须指定T的具体类型
public class Generic<T>{ 
    //key这个成员变量的类型为T,T的类型由外部指定  
    private T key;

    public Generic(T key) { //泛型构造方法形参key的类型也为T,T的类型由外部指定
        this.key = key;
    }

    public T getKey(){ //泛型方法getKey的返回值类型为T,T的类型由外部指定
        return key;
    }
}

//泛型的类型参数只能是类类型(包括自定义类),不能是简单类型
//传入的实参类型需与泛型的类型参数类型相同,即为Integer.
Generic<Integer> genericInteger = new Generic<Integer>(123456);

//传入的实参类型需与泛型的类型参数类型相同,即为String.
Generic<String> genericString = new Generic<String>("key_vlaue");
Log.d("泛型测试","key is " + genericInteger.getKey());
Log.d("泛型测试","key is " + genericString.getKey());
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值