1 包
包是组织类的一种方式,保证类的唯一性。
如果代码中有相同的类,就会编译报错。这时可以放到不同的包中。
1.1 导入包中的类——Java提供了很多现成的类:
public class Test {
public static void main(String[] args) {
java.util.Date date = new java.util.Date();//java.util包中的Date类
System.out.println(date.getTime());
}
}
或者写成import语句导入包
import java.util.Date;
public class Test {
public static void main(String[] args) {
Date date = new Date(); //更加简洁
System.out.println(date.getTime());
}
}
如果遇上要用多个java.util包中的类,可以写成:
import java.util.*
1.2 静态导入
使用import static导入包中的静态方法和字段,例如:
import static java.lang.Math.*;
public class Test {
public static void main(String[] args) {
double x = 30;
double y = 40;
// 静态导入的方式写起来更方便一些.
// double result = Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2));
double result = sqrt(pow(x, 2) + pow(y, 2));
System.out.println(result);
}
}
1.3 包中的访问权限
当输入int a = 10;时,int前没有标明权限时,默认为default,只能访问在同一个包中的信息,无论在包中是否在同一个类里。
2 继承
为了方便代码调用。将公共代码放到一个单独的类中,让其他的类可以继承这个单独的类。这个单独的类里面包含的属性和方法,都自动被新的类支持进去。同时,新的类可以有自己独特的扩展。子类会继承父类所有的属性和方法。
2.1 基本语法
class 子类 extends 父类{
}
注意,一个子类只能继承一个父类。子类的实例中,也包含着父类的实例,可以使用super关键字得到父类实例的引用。
2.2 protected
继承中的封装,用portected关键字,无论是同一包中的子类还是不同包中的子类,都可以调用字段和方法。只有不同包的非子类才不能调用。
2.3 final
理论上可以无限制的继承下去,但一般不超过3,并且可以在最后一个子类加上final来表示该类无法再被继承。
3 多态
“一个引用,能表现出多种不同的形态”。当父类引用子类对象,并且父类和子类有同名覆盖的方法,这时,如果通过父类引用调用这个重名方法,就会发生动态绑定。封装是为了不让调用者知道类的实现细节(但还是知道是个啥类),多态更进一步,只要知道有某个方法就行。
3.1 向上转型
一个父类的引用指向一个子类的实例。例如:
class Animal{ //Animal父类
protected String name;
public Animal(String name) {
this.name = name;
}
public void eat(String food){
System.out.println("我是一只小动物");
System.out.println(this.name + "吃" + food);
}
}
class Bird extends Animal{ //Bird子类
public Bird(String name) { //继承父类属性
super(name);
}
public void eat(String food){ //重写父类方法
System.out.println("我是一只小鸟");
System.out.println(this.name + "正在吃" + food);
}
public void fly(){ //子类特有方法
System.out.println(this.name + "正在飞");
}
}
public class test1 { //向上转型1
public static void main(String[] args) {
Animal bird = new Bird("小小鸟"); //直接赋值,父类引用指向子类实例,在这里发生向上转型
bird.eat("谷子"); //父类必须有eat方法 ,发生动态绑定,重写父类eat方法
}
}
运行结果为:
我是一只小鸟
小小鸟正在吃谷子
public class test2 { //向上转型2
public static void main(String[] args) {
Bird bird = new Bird("小小鸟");
feed(bird); //方法传参
}
public static void feed(Animal animal){
animal.eat("谷子");
}
}
运行结果为:
我是一只小鸟
小小鸟正在吃谷子
public class test3 { //向上转型3
public static void main(String[] args) {
Animal animal = findMyAnimal();//方法返回
animal.eat("小米");
}
public static Animal findMyAnimal(){
Bird bird = new Bird("圆圆");
return bird;
}
}
运行结果为:
我是一只小鸟
圆圆正在吃小米
3.2 动态绑定
子类与父类中同时出现同名的方法,具体执行时调用哪个方法,就要看这个引用指向的是父类对象还是子类对象,这个过程是程序运行时决定的(不是编译期),因此成为动态绑定。test1中有所体现。
3.3方法重写
重写也可以叫覆写/覆盖(Override)。与重载不同。重写中子类的方法访问权限不能低于父类的访问权限。针对重写,可以用@Override来显示指定。
class Shape{ //父类
public void draw(){ //父类方法
}
}
class Cycle extends Shape{
@Override //重写
public void draw() {
System.out.println("⚪");
}
}
class Triangle extends Shape{
@Override
public void draw() {
System.out.println("▲");
}
}
class Flower extends Shape{
@Override
public void draw() {
System.out.println("❀");
}
}
public class test1 {
public static void main(String[] args) {
Shape shape1 = new Flower();
Shape shape2 = new Cycle();
Shape shape3 = new Triangle();
drawShape(shape1);
drawShape(shape2);
drawShape(shape3);
}
public static void drawShape(Shape shape){
shape.draw();
}
}
最终结果为打印出三种形状。
使用多态的好处
(1)类的调用者对类的使用成本降低
(2)避免大量的if-else,降低全复杂度
(3)如需新增,改动成本低
3.4 向下转型
将父类的对象转为子类的对象
public static void main(String[] args) {
Animal animal = new Bird("圆圆");
animal.eat("谷子");
//animal.fly(); //错误
((Bird) animal).fly(); //强转成Bird型,但不能转成Cat型
//因为new出的是bird实例
编译过程中,animal的类型是Animal,因此,编译器只知道有一个eat方法,没有fly方法,虽然animal指向一个Bird对象,,但编译器是以animal的类型来查找方法。对于Animal animal = new Bird(“name”)这样的代码:(1)编译器检测有哪些方法的存在,只看Animal这个类型;(2)执行时到底执行父类还是子类,看的是Bird这个类型。
Animal animal1 = new Bird("扁扁");
if (animal1 instanceof Bird) { //always true
Bird bird = (Bird) animal1;
bird.fly();
}
System.out.println("=============");
Animal animal2 = new Cat("小猫");
if(animal2 instanceof Bird){ //always false
Bird bird1 = (Bird) animal2;
bird1.fly();
} // 不执行,new出的Cat实例,不是Bird
System.out.println("-----------");
}
instanceof可以判定一个引用是否是某个类的实例,是就返回true,执行if中的语句。
4 抽象类
在3.3中父类Shape中的draw方法没有实际工作,绘制方法都是由子类的draw方法实现。这样的父类可以设计成一个抽象方法,包含抽象方法的类就被成为抽象类。
abstract class Shape{
abstract public void draw();
}
draw方法前加上abstract关键字,表示这是一个抽象方法,同时抽象方法没有方法体(没有{},不能有具体执行的代码)对于包含抽象方法的类,必须加上abstract关键字表示这是一个抽象类。
注意:(1)抽象类不能直接实例化。
//Shape shape = new Shape(); //编译出错(2)抽象方法不能是private(3)抽象类中可以包含其他非抽象方法。
abstract class Shape2{ //抽象类
abstract public void draw(); //抽象方法,想要使用必须构建子类并在其中重写
void func(){ //非抽象方法
System.out.println("func()");
}
}
class Rect extends Shape2{
@Override
public void draw() { //实现(重写)抽象方法
System.out.println("draw(Rect)");
}
}
public class test8 {
public static void main(String[] args) {
Shape2 shape2 = new Rect(); //向上转型
shape2.func(); //非抽象
shape2.draw(); //抽象
}
}
运行结果:
func()
draw(Rect)
抽象类本身不能被实例化,要想使用,只能创建抽象类的子类,让子类重写抽象类中的抽象方法。
5 接口
接口是类的更进一步,抽象类中还可以包含非抽象方法,但接口中的方法都是抽象方法。字段只能包含静态常量。
interface IShape{
public abstract void draw(); //public abstract可以省略
public static final int num = 10; //public static final可以省略
}
class Cycle implements IShape{
@Override
public void draw() { //public不能省略
System.out.println("⚪");
}
}
public class test6 {
public static void main(String[] args) {
IShape shape = new Cycle();
shape.draw();
}
}
使用interface定义一个接口;方法一定是抽象的,可以省略abstract;方法一定是public的,可以省略public;Cycle用implements继承接口;调用时可以创建一个接口的引用,对应到一个子类的实例。接口不能被单独实例化。
在Java中只支持单继承,一个类只能extends一个父类,但可以同时实现多个接口,也能到达多继承类似的效果。
class Animal1{ //父类
private String name;
public Animal1(String name){
this.name = name;
}
}
interface IFlying{ //接口
void fly();
}
interface IRunning{
void run();
}
interface ISwimming{
void swim();
}
class Cat1 extends Animal implements IRunning{ // 先继承 再接口
public Cat1(String name) {
super(name);
}
@Override
public void run() {
System.out.println(this.name + "正在用四条腿跑");
}
}
class Fish extends Animal implements ISwimming{
public Fish(String name) {
super(name);
}
@Override
public void swim() {
System.out.println(this.name + "用尾巴游泳");
}
}
class Frog extends Animal implements IRunning, ISwimming{
public Frog(String name) {
super(name);
}
@Override
public void run() {
System.out.println(this.name + "往前跳");
}
@Override
public void swim() {
System.out.println(this.name + "用腿游泳");
}
}
class Robot implements IRunning{ //不用继承也可以实现接口
private String name;
protected Robot(String name) {
this.name = name;
}
@Override
public void run() {
System.out.println(this.name + "用轮子跑");
}
}
public class test7 {
public static void main(String[] args) {
Cat1 cat = new Cat1("小猫");
walk(cat);
Frog frog = new Frog("小青蛙");
walk(frog);
Robot robot = new Robot("机器");
walk(robot);
}
public static void walk(IRunning running){
System.out.println("去散步");
running.run();
}
}
接口之间也可以实现继承,甚至是多继承
interface IRunning{
void run();
}
interface ISwimming{
void swim();
}
interface IAmphibious extends IRunning,ISwimming{ //接口继承多个接口(extends)
}
class Frog implements IAmphibious{
//类和接口之间是实现(implements)
}
学生成绩排序
import java.util.Arrays;
class Student implements Comparable{
private String name;
private int score;
public Student(String name, int score) {
this.name = name;
this.score = score;
}
@Override
public String toString(){
return "[" + this.name + ":" + this.score + "]";
}
@Override
public int compareTo(Object o) {
Student s = (Student)o;
if (this.score > s.score){
return -1;
}else if(this.score < s.score){
return 1;
}else{
return 0;
}
}
}
public class test{
public static void main(String[] args) {
Student[] students = new Student[]{
new Student("zhangsan",95),
new Student("lisi",96),
new Student("wangwu",97),
new Student("zhaoliu",92),
};
Arrays.sort(students);
System.out.println(Arrays.toString(students));
}
}
附1 权限之间的区别
附2 重载与重写
附3 抽象类与接口