Java 面向对象
一.Java引用类型:
Java有 5种引用类型(对象类型):类 接口 数组 枚举 标注
引用类型:底层结构和基本类型差别较大
JVM的内存空间:(1). Heap 堆空间:分配对象 new Student()
(2). Stack 栈空间:临时变量 Student stu
(3).Code 代码区 :类的定义,静态资源 Student.class
eg:Student stu = new Student(); //new 在内存的堆空间创建对象
stu.study(); //把对象的地址赋给stu引用变量
上例实现步骤:a.JVM加载Student.class 到Code区
b.new Student()在堆空间分配空间并创建一个Student实例
c.将此实例的地址赋值给引用stu, 栈空间
二、构造方法:
class Person{
private String name;
private int age;
public Person(String name,int age){
this.age=age;
this.name=name;
}
public getName(){
……
}
……
}
三、重载overload
同C++
四、继承:extends
父类(parent class)、基类(base class)、超类(super class)
子类(subclass),扩展类(extended class)
##子类自动获得了父类的所有字段,严禁定义与父类重名的字段!
特点:
1.子类无法访问父类的private
字段或者private
方法,但可以访问protect字段或方法。
例如,Student
类就无法访问Person
类的name
和age
字段:
class Person {
private String name;
private int age;
}
class Student extends Person {
public String hello() {
return "Hello, " + name; // 编译错误:无法访问name字段
}
}
//正确使用:
class Person {
protected String name;
protected int age;
}
class Student extends Person {
public String hello() {
return "Hello, " + name; // OK!
}
}
protected
关键字可以把字段和方法的访问权限控制在继承树内部,一个protected
字段和方法可以被其子类,以及子类的子类所访问。
2.如果父类没有默认的构造方法,子类就必须显式调用super()
并给出参数以便让编译器定位到父类的一个合适的构造方法。
3.阻止继承:
正常情况下,只要某个class没有final
修饰符,那么任何类都可以从该class继承。
从Java 15开始,允许使用sealed
修饰class,并通过permits
明确写出能够从该class继承的子类名称。
例如,定义一个Shape
类:
public sealed class Shape permits Rect, Circle, Triangle {
...
}
上述Shape
类就是一个sealed
类,它只允许指定的3个类Rect, Circle, Triangle
继承它。
public final class Rect extends Shape {...}
4.把一个子类类型安全地变为父类类型的赋值,被称为向上转型(upcasting)。
向上转型实际上是把一个子类型安全地变为更加抽象的父类型:
Student s = new Student();
Person p = s; // upcasting, ok
Object o1 = p; // upcasting, ok
Object o2 = s; // upcasting, ok
5.继承是is关系,组合是has关系。
class Student extends Person {
protected Book book;
protected int score;
}
Student is person ,has book。
6.方法的重写规则
-
参数列表与被重写方法的参数列表必须完全相同。
-
返回类型与被重写方法的返回类型可以不相同,但是必须是父类返回值的派生类(java5 及更早版本返回类型要一样,java7 及更高版本可以不同)。
-
访问权限不能比父类中被重写的方法的访问权限更低。例如:如果父类的一个方法被声明为 public,那么在子类中重写该方法就不能声明为 protected。
-
父类的成员方法只能被它的子类重写。
-
声明为 final 的方法不能被重写。
-
声明为 static 的方法不能被重写,但是能够被再次声明。
-
子类和父类在同一个包中,那么子类可以重写父类所有方法,除了声明为 private 和 final 的方法。
-
子类和父类不在同一个包中,那么子类只能够重写父类的声明为 public 和 protected 的非 final 方法。
-
重写的方法能够抛出任何非强制异常,无论被重写的方法是否抛出异常。但是,重写的方法不能抛出新的强制性异常,或者比被重写方法声明的更广泛的强制性异常,反之则可以。
-
构造方法不能被重写。
-
如果不能继承一个类,则不能重写该类的方法。
继承的使用:
public class Main {
public static void main(String[] args) {
Student s = new Student();
s.run();
}
}
class Person {
public void run() {
System.out.print("person");
}
}
public class Student extends Person {
//@Override // Compile error!
public void run() {
super.run();
System.out.print("student!");
}
}
五、多态
多态的使用:
public class Main { public static void main(String[] args) { Person p = new Student(); p.run(); // 应该打印Person.run还是Student.run? } } class Person { public void run() { System.out.print("Person.run\n"); } } class Student extends Person { @Override public void run() {
System.out.printlin(super.run()+"!");//在复写方法中,使用super调用父类方法。 System.out.print("Student.run"); } }
用final
修饰的方法不能被Override;用
final
修饰的类不能被继承。
对于一个类的实例字段,同样可以用final
修饰。用final
修饰的字段在初始化后不能被修改:
在构造方法中初始化final字段: class Person { public final String name; public Person(String name) { this.name = name; } } 这种方法更为常用,因为可以保证实例一旦创建,其final字段就不可修改。
六、抽象类:
如果父类的方法本身不需要实现任何功能,仅仅是为了定义方法签名,目的是让子类去覆写它,那么,可以把父类的方法声明为抽象方法,抽象方法用abstract
修饰。
因为无法执行抽象方法,因此这个类也必须申明为抽象类(abstract class)。
使用abstract
修饰的类就是抽象类。我们无法实例化一个抽象类,因为抽象类本身被设计成只能用于被继承,因此,抽象类可以强迫子类实现其定义的抽象方法,否则编译会报错。因此,抽象方法实际上相当于定义了“规范”。
public class Main { public static void main(String[] args) { Person p = new Student(); p.run(); } } abstract class Person { public abstract void run(); } class Student extends Person { @Override public void run() { System.out.println("Student.run"); } }
面向抽象编程的本质就是:
-
上层代码只定义规范(例如:
abstract class Person
); -
不需要子类就可以实现业务逻辑(正常编译);
-
具体的业务逻辑由不同的子类实现,调用者并不关心。
Person s = new Student(); Person t = new Teacher(); s.run(); t.run(); //同样的代码,如果引用的是一个新的子类,同样不关心新的子类是如何实现run()方法的: Person e = new Employee(); e.run(); 这种尽量引用高层类型,避免引用实际子类型的方式,称之为面向抽象编程。这种引用抽象类的好处在于,我们对其进行方法调用,并不关心Person类型变量的具体子类型
-
定义了抽象方法的class必须被定义为抽象类,从抽象类继承的子类必须实现抽象方法;
-
如果不实现抽象方法,则该子类仍是一个抽象类;
-
面向抽象编程使得调用者只关心抽象方法的定义,不关心子类的具体实现。
七、接口interface
类继承接口使用implements
接口继承接口使用extends
因为interface
是一个纯抽象类,所以它不能定义实例字段。但是,interface
是可以有静态字段的,并且静态字段必须为final
类型:
public interface Person { public static final int MALE = 1; public static final int FEMALE = 2; }
public interface Person { // 编译器会自动加上public statc final: int MALE = 1; int FEMALE = 2; }
若去掉修饰符,编译器会自动把该字段变为public static final
类型。
八、静态字段和静态方法static
在一个class
中定义的字段,我们称之为实例字段。实例字段的特点是,每个实例都有独立的字段,各个实例的同名字段互不影响。
实例字段在每个实例中都有自己的一个独立“空间”,但是静态字段只有一个共享“空间”,所有实例都会共享该字段。
对于静态字段,无论修改哪个实例的静态字段,效果都是一样的:所有实例的静态字段都被修改了,原因是静态字段并不属于实例:
在代码中,实例对象能访问静态字段只是因为编译器可以根据实例类型自动转换为类名.静态字段
来访问静态对象,所以推荐用类名来访问静态字段。
Parent.fun();
关于final:
修饰方法:
如果只有在想明确禁止 该方法在子类中被覆盖的情况下才将方法设置为final的。
注:类的private方法会隐式地被指定为final方法。
修饰变量
修饰变量是final用得最多的地方,也是本文接下来要重点阐述的内容。首先了解一下final变量的基本语法:
对于一个final变量,如果是基本数据类型的变量,则其数值一旦在初始化之后便不能更改;如果是引用类型的变量,则在对其初始化之后便不能再让其指向另一个对象。
很多时候会容易把static和final关键字混淆,static作用于成员变量用来表示只保存一份副本,而final的作用是用来保证变量不可变。看下面这个例子:
public class Test { public static void main(String[] args) { MyClass myClass1 = new MyClass(); MyClass myClass2 = new MyClass(); System.out.println(myClass1.i); System.out.println(myClass1.j); System.out.println(myClass2.i); System.out.println(myClass2.j); } } class MyClass { public final double i = Math.random(); public static double j = Math.random(); }
运行这段代码就会发现,每次打印的两个j值都是一样的,而i的值却是不同的。
九、jar包
jar包可以把package
组织的目录层级,以及各个目录下的所有文件(包括.class
文件和其他文件)都打成一个jar文件
jar包实际上就是一个zip格式的压缩文件,而jar包相当于目录。如果我们要执行一个jar包的class
,就可以把jar包放到classpath。所以,
jar
文件就是class
文件的容器。
jar包里的第一层目录,不能是bin
,而应该是hong
、ming
、mr,如果在Windows的资源管理器中看,应该长这样:
ar包还可以包含一个特殊的/META-INF/MANIFEST.MF
文件,MANIFEST.MF
是纯文本,可以指定Main-Class
和其它信息。JVM会自动读取这个MANIFEST.MF
文件,如果存在Main-Class
,我们就不必在命令行指定启动的类名,而是用更方便的命令:java -jar hello.jar
jar包还可以包含其它jar包,这个时候,就需要在MANIFEST.MF
文件里配置classpath
了。
在大型项目中,不可能手动编写MANIFEST.MF
文件,再手动创建zip包。Java社区提供了大量的开源构建工具,例如Maven,可以非常方便地创建jar包。