基础
输入
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
String name = scanner.next();
int age = scanner.nextInt();
double weight = scanner.nextDouble();
boolean isSingle = scanner.nextBoolean();
char gender = scanner.next().charAt(0);
scanner.close();
}
}
二维数组静态初始化
int[][] arr1 = {{1, 2, 3}, {4, 5, 6}, {7, 8, 9, 10}};
int[][] arr2 = new int[][]{{1, 2, 3}, {4, 5, 6}, {7, 8, 9, 10}};
int[][] arr3 = new int[3][3]{{1, 2, 3}, {4, 5, 6}, {7, 8, 9, 10}}; //错误,静态初始化右边new 数据类型[][]中不能写数字
length
length——数组的属性;
length()——String的方法;
size()——集合的方法;
String[] list={"a","b","c"};
System.out.println(list.length); //数组用length
String a="apple";
System.out.println(a.length()); //String用length()
List<String> list = new ArrayList<String>();
list.add("a");
list.add("b");
list.add("c");
System.out.println(list.size());//集合用size()
面向对象编程
protected
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
字段和方法可以被其子类,以及子类的子类所访问
super
class Person {
protected String name; // protected之后,可以被子类访问
protected int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
}
class Student extends Person { //继承Person类
protected int score;
public Student(String name, int age, int score) {
super(name, age); // 必须super来调用父类的构造函数
this.score = score;
}
}
阻止继承
正常情况下,只要某个class没有final
修饰符,那么任何类都可以从该class继承。
从Java 15开始,允许使用sealed
修饰class,并通过permits
明确写出能够从该class继承的子类名称。
例如,定义一个Shape
类:
public sealed class Shape permits Rect, Circle, Triangle {
...
}
上述Shape
类就是一个sealed
类,它只允许指定的3个类继承它。如果写:
public final class Rect extends Shape {...}
是没问题的,因为Rect
出现在Shape
的permits
列表中。但是,如果定义一个Ellipse
就会报错:
public final class Ellipse extends Shape {...}
// Compile error: class is not allowed to extend sealed class: Shape
原因是Ellipse
并未出现在Shape
的permits
列表中。这种sealed
类主要用于一些框架,防止继承被滥用。
sealed
类在Java 15中目前是预览状态,要启用它,必须使用参数--enable-preview
和--source 15
。
向上转型
Student s = new Student(); // 声明一个学生类
Person p1 = new Student(); // 向上转型
Person p2 = s; // 向上转型
Object o1 = p2; // 向上转型
Object o2 = s2; // 向上转型
向下转型
必须用instance来判断一下
Object obj = "hello";
if (obj instanceof String s) { // 从Java 14开始,判断instanceof后,可以直接转型为指定变量,避免再次强制转型。
// 可以直接使用变量s:
System.out.println(s.toUpperCase());
覆写(override)
覆写override与重载overload不同,前者是子类覆写父类的相同方法,例如:
class Person {
public void run() {
System.out.println("Person.run");
}
}
class Student extends Person{
@override // 加上@Override可以让编译器帮助检查是否进行了正确的覆写。希望进行覆写,但是不小心写错了方法签名,编译器会报错。但是不是必须的
public void run() {
System.out.println("Student.run")
}
}
注意,以下情况不属于override
class Person {
public void run() { … }
}
class Student extends Person {
// 不是Override,因为参数不同:
public void run(String s) { … }
// 不是Override,因为返回值不同:
public int run() { … }
}
多态
多态的功能:允许添加更多类型的子类实现功能扩展,却不需要修改基于父类的代码。
覆写Object方法
因为所有的class
最终都继承自Object
,而Object
定义了几个重要的方法:
toString()
:把instance输出为String
;equals()
:判断两个instance是否逻辑相等;hashCode()
:计算一个instance的哈希值。
在必要的情况下,我们可以覆写Object
的这几个方法。例如:
final
final
修饰的类不可以被继承,final
的方法不能被override。
用final
修饰的字段在初始化后不能被修改,可以在构造方法中初始化final
字段,一旦实例创建,final
字段就不可修改
abstract
如果父类的方法本身不需要实现任何功能,仅仅是为了定义方法签名,目的是让子类去覆写它,那么,可以把父类的方法声明为抽象方法:
class Person {
public abstract void run();
}
把一个方法声明为abstract
,表示它是一个抽象方法,本身没有实现任何方法语句。因为这个抽象方法本身是无法执行的,所以,Person
类也无法被实例化。编译器会告诉我们,无法编译Person
类,因为它包含抽象方法。
必须把Person
类本身也声明为abstract
,才能正确编译它:
abstract class Person {
public abstract void run();
}
抽象类
如果一个class
定义了方法,但没有具体执行代码,这个方法就是抽象方法,抽象方法用abstract
修饰。
因为无法执行抽象方法,因此这个类也必须申明为抽象类(abstract class)。
使用abstract
修饰的类就是抽象类。我们无法实例化一个抽象类:
Person p = new Person(); // 编译错误
无法实例化的抽象类有什么用?
因为抽象类本身被设计成只能用于被继承,因此,抽象类可以强迫子类实现其定义的抽象方法,否则编译会报错。因此,抽象方法实际上相当于定义了“规范”。
例如,Person
类定义了抽象方法run()
,那么,在实现子类Student
的时候,就必须覆写run()
方法:
面向抽象编程
本质就是:
- 上层代码只定义规范(例如:
abstract class Person
); - 不需要子类就可以实现业务逻辑(正常编译);
- 具体的业务逻辑由不同的子类实现,调用者并不关心。
接口
在抽象类中,抽象方法本质上是定义接口规范:即规定高层类的接口,从而保证所有子类都有相同的接口实现,这样,多态就能发挥出威力。
如果一个抽象类没有字段,所有方法全部都是抽象方法时:
abstract class Person {
public abstract void run();
public abstract String getName();
}
就可以把该抽象类改写为接口: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
...
}
接口继承
一个interface
可以继承自另一个interface
。interface
继承自interface
使用extends
,它相当于扩展了接口的方法。例如:
interface Hello {
void hello();
}
interface Person extends Hello {
void run();
String getName();
}
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() { //必须要有,因为person中getName没有加default
return this.name;
}
}
实现类可以不必覆写default
方法。default
方法的目的是,当我们需要给接口新增一个方法时,会涉及到修改全部子类。如果新增的是default
方法,那么子类就不必全部修改,只需要在需要覆写的地方去覆写新增方法。
静态字段和静态方法
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
// 不推荐上述方法访问静态字段,推荐用类名来访问静态字段
Person.number = 100;
System.out.println(Person.number); // 100
}
}
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 class Main {
public static void main(String[] args) {
Person.setNumber(99);
System.out.println(Person.number); // 可以直接通过
}
}
class Person {
public static int number;
public static void setNumber(int value) { // 静态方法类似于其他编程语言的函数
number = value;
}
}
接口的静态字段
因为interface
是一个纯抽象类,所以它不能定义实例字段。但是,interface
是可以有静态字段的,并且静态字段必须为final
类型:
public interface Person {
public static final int MALE = 1;
public static final int FEMALE = 2;
}
实际上,因为interface
的字段只能是public static final
类型,所以我们可以把这些修饰符都去掉,上述代码可以简写为:
public interface Person {
// 编译器会自动加上public statc final:
int MALE = 1;
int FEMALE = 2;
}
编译器会自动把该字段变为public static final
类型。