文章目录
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());