在java中,除了基本的数据类型,其他的基本都是类,
比如接口,异常等等…
构造函数
和c++类似,构造函数是用来初始化的实例中的字段的,实例就是对象,字段在这里代表的是类中的数据.没有返回值,而且和类名相同.
比如:
class Person {
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return this.name;
}
public int getAge() {
return this.age;
}
}
这里的Person(String name ,int age)就是一个构造函数,调用构造函数必须用new:
Person p = new Person("Xiao Ming", 15);
System.out.println(p.getName());
System.out.println(p.getAge());
和c++类似,java类中都会有默认构造函数,没有参数,也没有执行语句:
class Person {
public Person() {
}
}
通过Person p = new Person()来创建一个实例,但是如果已经定义了其他的构造函数,那么就不能调用默认构造函数,想要都能使用两个构造函数,只能够把两个构造函数都定义起来.
和c++类似,java中也是有this,功能差不多,都是指向自己的实例.
class Person {
private String name;
private int age;
public Person() {
}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return this.name;
}
public int getAge() {
return this.age;
}
}
和c++不太像的是,java类中的字段可以赋值的,如果没有赋值,引用类型的初始值就是null,数值是0,布尔类型就是false;
在java中,创建实例的时候是按照下列顺序进行初始化的:
1.始化字段,例如,int age = 10;表示字段初始化为10,double salary;表示字段默认初始化为0,String name;表示引用类型字段默认初始化为null;
2.构造方法的代码进行初始化。
和c++类似,允许定义多个构造方法,用new操作符调用的时候,通过构造方法的参数数量,数据类型和类型自动区分的.
方法重载
和c++类似,java中可以重载函数,函数名相同,但是各自的参数不同,这就是函数重载.
class Hello {
public void hello() {
System.out.println("Hello, world!");
}
public void hello(String name) {
System.out.println("Hello, " + name + "!");
}
public void hello(String name, int age) {
if (age < 18) {
System.out.println("Hi, " + name + "!");
} else {
System.out.println("Hello, " + name + "!");
}
}
}
在java中,hello类中的返回值都必须是一样的,只有参数不太一样.
继承
在java中,继承基本上都是满足is-a关系的,用extends来实现继承,通过继承来达到代码复用的一个目的.
class Person {
private String name;
private int age;
public String getName() {...}
public void setName(String name) {...}
public int getAge() {...}
public void setAge(int age) {...}
}
class Student extends Person {
// 不要重复name和age字段/方法,
// 只需要定义新增score字段/方法:
private int score;
public int getScore() { … }
public void setScore(int score) { … }
}
不要重新写name,age这些了,直接继承到了子类.而且在java中,如果没有明确写继承哪个类,编译器都会自动加上extends Object,除了Object之外,都会继承一个类.继承树如下:
和c++不同的是,java只允许一个class继承自一个类,一个类只有一个父类,Object没有父类.
在继承中,子类无法访问父类私有的数据或者私有方法.和c++类似,就出现了protected,可以在子类中使用父类protected中的方法数据,但是在外部还是无法使用protected方法.
例如:
class Person {
protected String name;
protected int age;
}
class Student extends Person {
public String hello() {
return "Hello, " + name; // OK!
}
}
super关键字代表的是父类,指向父类.和c++不太一样,java中,子类继承父类,因该是先有父类才有的子类,所以需要对父类先进行初始化,在java中,任何类的构造方法第一条语句就是调用父类的构造方法,如果没有明确的调用父类的构造方法,就会自动加上一句super().如果已经有带参数的构造方法,没有不带参数的构造函数,就会编译失败.
示例类如下:
class Person {
protected String name;
protected int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
}
class Student extends Person {
protected int score;
public Student(String name, int age, int score) {
this.score = score;
}
如果直接创建一个Student类对象,则会失败,因为父类没有创建:
Student s = new Student("Xiao Ming", 12, 89); // 失败
如果将Student类中的构造函数改成第一句加上super(),也会失败,因为父类没有不带参数的构造函数:
class Student extends Person {
protected int score;
public Student(String name, int age, int score) {
super(); // 自动调用父类的构造方法
this.score = score;
}
}
所以应该改为:
class Student extends Person {
protected int score;
public Student(String name, int age, int score) {
super(name, age); // 调用父类的构造方法Person(String, int)
this.score = score;
}
}
向上和向下转型
和c++类似,向上转型就是一个引用类型为某个类的变量,可以指向其子类的引用变量
Person p = new Student(); // OK!
向下转型就是进行强制转换
Person p1 = new Student(); // upcasting, ok
Person p2 = new Person();
Student s1 = (Student) p1; // ok
Student s2 = (Student) p2; // runtime error! ClassCastException!
应为p1本来就是Student的实例,当然可以直接强制转换赋值给s1,但是p2不行,这是符合面向对象is-a思想的.在java13中,利用instanceof来判断是否属于这个类的.
Person p = new Person();
System.out.println(p instanceof Person); // true
System.out.println(p instanceof Student); // false
Student s = new Student();
System.out.println(s instanceof Person); // true
System.out.println(s instanceof Student); // true
Student n = null;
System.out.println(n instanceof Student); // false
多态
多态其实就是在某个实例调用方法的时候, 真正执行的方法取决于运行时的实际方法.
重写方法是实现多态的一种手段,重写是重新写父类的函数,其中返回值,参数,函数名都不变,重载是在一个类中函数名不变,参数变.
public class Main {
public static void main(String[] args) {
Person p = new Student();
p.run(); // 执行Student中的run函数.
}
}
class Person {
public void run() {
System.out.println("Person.run");
}
}
class Student extends Person {
@Override
public void run() {
System.out.println("Student.run");
}
}
@Override是为了检查是否正确重写.加上final修饰词的类不允许继承.实例如下:
final class Person {
protected String name;
}
// compile error: 不允许继承自Person
Student extends Person {
}
抽象类
抽象类类似于c++中的ABC,在类前面用abstract和抽象类中的方法也用abstract修饰,继承抽象类的子类必须重写抽象方法,如下所示:
abstract class Person {
public abstract void run();
}
class Student extends Person {
@Override
public void run() {
System.out.println("Student.run");
}
}
通过尽量高的父类来实例化一个对象,避免引用实际的子类,称为面向抽象编成.
Person s = new Student();
Person t = new Teacher();
Person e = new Employee();
s.run();
t.run();
e.run();
以上代码不用关心具体类型,是面向抽象编程.
接口
如果一和抽象类没有具体的数据类型,所有的内容都是抽象方法,就可以改写为接口,接口也是类,只不过用interface表示,然后通过implements实现.用new实例化接口,和抽象类的用法很像.例如:
interface Person {
void run();
String getName();
}
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;
}
}
接口也是类,也可以继承
interface Hello {
void hello();
}
interface Person extends Hello {
void run();
String getName();
}
default方法可以在接口中实现,存在default方法是因为,如何借口中新增加一个方法,这个接口有很多子类,就必要要逐一添加新方法并实现,而default只需要重写就行了.实现default 的方法只需要在接口方法前面加上default就行了.
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;
}
}
静态字段和方法.
具体的字段的在独立的空间中,而静态的字段都共享一片空间.
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); // 88
hong.number = 99;
System.out.println(ming.number); // 99
}
}
class Person {
public String name;
public int age;
public static int number;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
}
那个具体的对象改变静态字段,所有对象的静态字段也都被修改了,原因是静态字段不是属于每个具体对象,而是属于一个共享的空间.存储示意图如下:
所以直接用类名.静态字段就可以调用了,静态方法只能用静态字段.而且接口中也可以定义静态字段:
public interface Person {
public static final int MALE = 1;
public static final int FEMALE = 2;
}
不加public static final,编译器也会自动加上.
包
eclipse 下面的folder,Source Folder,package的区别与作用?
folder就是普通的文件夹,而Source Folder拥有普通folder的所有功能,并且Source Folder下的所有java文件都会被编译,package也是特殊的文件夹,package必须放在Source Folder下,存放类,用.隔开,生成相应的包路径.
包类似于c++中的名称空间,用来防止类名冲突的,包就是拿来放类用的,一个类总是属于一个包,所有的java程序都是在包中.如果使用IDE,会自动创建一个Source Folder,在Source Folder可以创建包,使用IDE,在创建包的时候用.隔开就是一个层级,说白了,package,Source Folder,Folder都是目录,只不过职能不同.
文件结构:
实现上面的文件结构,前两个结构很容易,最后需要在包下面创建一个包,就可以用.隔开,如下:
实际上用.隔开,已经创建了mr/jun的包结构
包作用域
在一个类中,我们总会引用其他的类,使用import引入.用其它包的类有三种写法:
比如引进 mr/jun/下的Arrays类,直接在引用变量前面加上完整的类名:mr.jun.Arrays如下,
public class Person {
public void run() {
mr.jun.Arrays arrays = new mr.jun.Arrays();
}
}
或者用import,
import mr.jun.Arrays;
public class Person {
public void run() {
mr.jun.Arrays arrays = new mr.jun.Arrays();
}
}
或者全部引入,
import mr.jun.*;
public class Person {
public void run() {
mr.jun.Arrays arrays = new mr.jun.Arrays();
}
}
最终编译出来的结果都是加上完整类名,引用类也是有顺序的,如下:
1.如果是完整类名,就直接根据完整类名查找这个class;
如果是简单类名,按下面的顺序依次查找:
2.查找当前package是否存在这个class;
3.查找import的包是否包含这个class;
4.查找java.lang包是否包含这个class。java.lang是基础包
都没找到,则报错.
包作用域是指一个类允许访问同一个包中的没有修饰符修饰的类,以及没有修饰符修饰的字段和方法.包必须完全一致,不能有上下级关系.