多态的几种形式:
具体类多态(几乎不用)
抽象类多态(常用)
接口多态(最常用)
抽象类:
A:抽象类和抽象方法必须使用abstract关键字修饰
B:抽象类中不一定有抽象方法,有抽象方法的类一定是抽象类
C:抽象类不能实例化
因为是抽象类,它是抽象的,没有具体的执行,不能直接实例化
比如动物抽象类,有一个抽象的吃的方法,是真的抽象,没有告诉你具体吃什么
你实例化出来一只猫,还不得饿死,极其残忍。
抽象类如何实例化呢?
只能由子类重写抽象方法,使其具体化,才能够间接实例化
参照多态的方式,父类引用指向子类对象,通过子类对象实例化。
理解:
抽象类的目的就是为了抽象,抽象出对象必要的共性,由其子类体现个性
动物为了生存,就必须觅食,那么动物抽象类,就有一个吃的抽象方法,只要你是要吃的,你就是动物类的对象
如果你知道自己要吃鱼(重写抽象方法),那么你就是一只活的猫(具体动物类-猫类)
只要你是要吃的(抽象动物类),不管你是吃鱼还是啃骨头(多态)站着躺着坐着跪着(子类新增特有方法)
你也还是动物,不是石头(抽象类的作用给出必要的限定,不能让动物活生生地饿死)
一只独一无二的上进的猫(实例化对象),想学学习一下高阶远古遗忘技能猫捉老鼠(接口)
就来黑猫培训(接口实现类),帮你走上猫生巅峰(接口实现)
这里有来自全国各地的喵,一快乐地起捉老鼠
敬一群沉迷学习无法自拔的编程猫(接口是诗和远方,不是水和面包)
抽象类的子类
要么重写抽象类中的所有抽象方法
要么是抽象类
因为抽象类的子类
会继承父类的抽象方法
而有抽象方法的类,又必须是抽象类
抽象类的成员特点:
成员变量:
有成员变量,成员变量可以是变量,也可以是常量。
构造方法:
有构造方法。
抽象类中构造方法的作用?
用于子类访问父类数据的初始化。
成员方法:
有成员方法,成员方法可以是抽象的,也可以是非抽象的。
抽象方法:限定子类必须完成某些动作
非抽象方法:提高代码的复用性
接口:
额外扩展的功能
接口的特点:
A:定义接口使用的是interface关键字
B:类和接口之间是实现关系,用implements关键字表示
C:接口不能实例化
接口有没有其他的方式实例化呢?
参照多态的形式使用实现类来实例化。
D:接口的实现类
要么重写接口中的所有的抽象方法
要么是一个抽象类
接口成员的特点:
成员变量:
有成员变量,变量只能是常量并且是静态的。
默认修饰符:public static final
构造方法:
没有构造方法
接口不是类,成员变量只能是静态的常量,所以压根不需要初始化
成员方法:
有成员方法,而且都是抽象的。
默认修饰符:public abstract
接口实现类:
默认继承Object父类或者可以显示继承父类
Object是类层次结构的根类,所有的类都直接的或者间接的继承自该类。
对象、具体类、抽象类、接口之间的联系与区别
抽象的程度由弱变强
对象——>具体类——>抽象类——>接口
类与类:
继承关系,只能单继承,可以多层继承。
类与接口:
实现关系,可以单实现,也可以多实现。
还可以在继承一个类的同时实现多个接口。
接口与接口:
继承关系,可以单继承,也可以多继承。
抽象类和接口的区别:
A:成员区别
抽象类:
成员变量:可以是变量,也可以是常量
构造方法:有
成员方法:可以是抽象方法,也可以是非抽象方法
接口:
成员变量:只能是静态常量
成员方法:只能是抽象方法
B:关系区别
类与类:继承关系,只能单继承,可以多层继承
类与接口:实现关系,可以单实现,也可以多实现
接口与接口:继承关系,可以单继承,也可以多继承
C:设计理念的区别
抽象类 被继承体现的是:"is a" 抽象类中定义的是继承体系的共性功能
接口 被实现体现的是:"like a" 接口中定义的是该体系的扩展功能
举例:
猫,动物
猫,跳高
/*
* 需求:猫狗案例,让所有的猫狗具备跳高的额外功能
*
* 分析:从具体到抽象
* 猫:姓名,年龄,捉老鼠(){},吃饭(){}
* 狗:姓名,年龄,看门(){},吃饭(){}
* 发现了共性的内容,就提取了一个父类。
* 抽象动物类:
* 姓名,年龄,吃饭();
* 猫:继承抽象动物类
* 重写抽象方法
* 添加子类特有方法
* 狗:继承抽象动物类
* 重写抽象方法
* 添加子类特有方法
* 跳高的额外功能是一个扩展功能,所以应该定义接口实现。
* 跳高接口:
* 跳高();
* 猫:继承抽象动物类,实现跳高接口
* 狗:继承抽象动物类,实现跳高接口
* 实现:从抽象到具体
* 使用:使用的是具体的类的对象
*
*/
public class InterfaceTest {
public static void main(String[] args) {
//具体类多态(参见上一篇帖子-多态)
//定义本类引用,创建本类对象
Cat c = new Cat("三脚猫",3);
//本类引用调用本类对象方法
c.eat();//3岁的三脚猫 在吃鱼
c.catchMouse();
c.jump();//3岁的三脚猫 可以跳高了
Dog d = new Dog("藏獒", 9);
d.eat();//9岁的藏獒 在啃骨头
d.lookDoor();//9岁的藏獒 在看门
d.jump();//9岁的藏獒 可以跳高了
//抽象类多态
//父类引用指向子类对象
Animal a = new Cat("狸花猫",2);
a.eat();//2岁的狸花猫 在吃鱼
a = new Dog("乡村土狗", 8);
a.eat();//8岁的乡村土狗 在啃骨头
//接口多态
//接口引用指向子类对象(向上转型)
Jumpping j = new Cat("挪威森林猫",1);
j.jump();//1岁的挪威森林猫 可以跳高了
j = new Dog("德国牧羊犬", 7);
j.jump();//7岁的德国牧羊犬 可以跳高了
//接口类型转换(向下转型)
System.out.println("-------------接口类型转换---------------");
Jumpping j2 = new Cat("挪威森林猫2",1);
j2.jump();//可以跳高了
//编译报错
//The method eat() is undefined for the type Jumpping
//j2.eat();
//The method catchMouse() is undefined for the type Jumpping
//j2.catchMouse();
Cat cat = (Cat)j2;
cat.jump();//可以跳高了
cat.eat();//在吃鱼
//向下转型 运行子类特有方法
cat.catchMouse();//在捉老鼠
//运行报错
//com.sz_09.Cat cannot be cast to com.sz_09.Dog
//Dog dog = (Dog)j2;
//dog.jump();
}
}
//跳高接口
public interface Jumpping {
public abstract void jump();
}
//抽象的动物类
public abstract class Animal {
private String name;
private int age;
public Animal() {}
public Animal(String name,int age){
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public abstract void eat();
}
//具体的猫类
public class Cat extends Animal implements Jumpping {
public Cat() {
}
public Cat(String name, int age) {
super(name, age);
}
@Override
public void jump() {
System.out.println(super.getAge()+"岁的"+super.getName()+" 可以跳高了");
}
@Override
public void eat() {
System.out.println(super.getAge()+"岁的"+super.getName()+" 在吃鱼");
}
public void catchMouse(){
System.out.println(super.getAge()+"岁的"+super.getName()+" 在捉老鼠");
}
}
//具体的狗类
public class Dog extends Animal implements Jumpping {
public Dog() {
}
public Dog(String name, int age) {
super(name, age);
}
@Override
public void jump() {
System.out.println(super.getAge()+"岁的"+super.getName()+" 可以跳高了");
}
@Override
public void eat() {
System.out.println(super.getAge()+"岁的"+super.getName()+" 在啃骨头");
}
public void lookDoor(){
System.out.println(super.getAge()+"岁的"+super.getName()+" 在看门");
}
}
常用API
Object
/*
* Object:是类层次结构的根类,所有的类都直接的或者间接的继承自该类。
* 构造方法:Object()
* Object 类的 toString 方法返回一个字符串,该字符串由类名(对象是该类的一个实例)
* at 标记符“@”和此对象哈希码的无符号十六进制表示组成(地址)
*
* 返回该对象的字符串表示(地址)
*
* 通常,toString 方法会返回一个“以文本方式表示”此对象的字符串
*
* 结果应是一个简明但易于读懂的信息表达式
*
* 建议所有子类都重写此方法
* 直接输出对象名,输出底层调用的是该对象的toString()
*
* 查看API,我们发现建议所有子类都重写toString()。
* 到底该如何重写该方法呢?自动生成的就可以。
*/
public class ObjectDemo {
public static void main(String[] args) {
Student s = new Student();
s.setName("林青霞");
s.setAge(30);
System.out.println(s);
//System.out.println(s.toString());
/*
public void println(Object x) { //Object x = s;
String s = String.valueOf(x);
synchronized (this) {
print(s);
newLine();
}
}
public static String valueOf(Object obj) { //Object obj = x;
return (obj == null) ? "null" : obj.toString();
}
*/
}
}
String
/*
* String:代表字符串类。
* 由多个字符组成的一串数据。
* 字符串的本质就是一个字符数组。
*
* 构造方法:
* String(String original):把字符串数据封装成字符串对象
* String(char[] value):把字符数组的数据封装成字符串对象
* String(char[] value, int index, int count):把字符数组的一部分数据封装成字符串对象
*
* public String toString():返回此对象本身(它已经是一个字符串!)。
*/
public class StringDemo {
public static void main(String[] args) {
String st1 = null;
/*
public static String valueOf(Object obj) { //Object obj = x;
return (obj == null) ? "null" : obj.toString();
}
*
*/
System.out.println(st1);//null
String st2 = " ";
System.out.print(st2);
System.out.println('s');
System.out.println("sss----------");
//String(String original):把字符串数据封装成字符串对象
String s1 = new String("hello");
System.out.println(s1);
System.out.println("----------");
//String(char[] value):把字符数组的数据封装成字符串对象
char[] value = {'h','e','l','l','o'};
String s2 = new String(value);
System.out.println(s2);
System.out.println("----------");
//String(char[] value, int index, int count):把字符数组的一部分数据封装成字符串对象
//String s3 = new String(value,0,value.length);
String s3 = new String(value,0,3);
System.out.println(s3);
System.out.println("----------");
//最常用的
String s4 = "hello";
System.out.println(s4);
}
}
/*
* String类创建对象的特点:
* A:通过构造方法创建对象
* B:通过直接赋值的方式创建对象
* 这两种方式的创建是有区别的。
* 通过构造方法创建的字符串对象是在堆内存。
* 通过直接赋值的方式创建的字符串对象是在方法区的常量池。
*/
public class StringDemo2 {
public static void main(String[] args) {
String s1 = new String("hello");
String s2 = new String("hello");
String s3 = "hello";
String s4 = "hello";
System.out.println(s1 == s2);//false
System.out.println(s1 == s3);//false
System.out.println(s3 == s4);//true
}
}
StringBuilder
/*
* StringBuilder和String的相互转换
*
* StringBuilder -- String
* public String toString():通过toString()就可以实现把StringBuilder转换为String
*
* String -- StringBuilder
* public StringBuilder(String s):通过构造方法就可以实现把String转换为StringBuilder
*/
public class StringBuilderTest {
public static void main(String[] args) {
//StringBuilder -- String
StringBuilder sb = new StringBuilder();
sb.append("hello");
//错误的
//String s = sb;
//public String toString()
String s1 = sb.toString();
System.out.println(s1);
//String -- StringBuilder
String s = "hello";
StringBuilder sb1 = new StringBuilder(s);
System.out.println(sb1);
}
}
public class Test {
public static void main(String[] args) {
String s = "a";
s += "b";
s += "c";
s += "hello";
//用工具反编译class文件后的底层代码
/*
public static void main(String args[])
{
String s = "a";
s = (new StringBuilder(String.valueOf(s))).append("b").toString();
s = (new StringBuilder(String.valueOf(s))).append("c").toString();
s = (new StringBuilder(String.valueOf(s))).append("hello").toString();
}
*/
}
}
Arrays
/* Arrays类有构造方法吗?
* 通过查源代码
* private Arrays() {}
*
* Arrays 有构造方法,只不过被private修饰,外界无法使用访问
* 因为外界无法使用访问,所以API中看不到
* Arrays 工具类的设计思想
* 构造方法私有
* 成员都用static 修饰
* 直接使用类名访问使用
*
* Arrays:提供了对数组操作的各种方法。
* public static String toString(int[] a):把数组转成字符串
* public static void sort(int[] a):对数组进行升序排序
*/
public class ArraysDemo2 {
public static void main(String[] args) {
//定义一个数组
int[] arr = {24,69,80,57,13};
//public static String toString(int[] a):把数组转成字符串
System.out.println("排序前:"+Arrays.toString(arr));
//public static void sort(int[] a):对数组进行升序排序
Arrays.sort(arr);
System.out.println("排序后:"+Arrays.toString(arr));
}
}
Integer
/*
* JDK5新特性:
* 自动装箱:把基本数据类型转换为对应的包装类类型
*
* Returns an {@code Integer} instance representing the specified
* {@code int} value. If a new {@code Integer} instance is not
* required, this method should generally be used in preference to
* the constructor {@link #Integer(int)}, as this method is likely
* to yield significantly better space and time performance by
* caching frequently requested values.
*
* This method will always cache values in the range -128 to 127,
* inclusive, and may cache other values outside of this range.
*
* @param i an {@code int} value.
* @return an {@code Integer} instance representing {@code i}.
* @since 1.5
public static Integer valueOf(int i)
* 自动拆箱:把包装类类型转换为对应的基本数据类型
* Returns the value of this {@code Integer} as an
* {@code int}.
public int intValue()
*
* Java程序的运行:
* 编写java文件 -- 编译生成class文件 -- 执行
*
* 注意:在使用包装类类型的新特性的时候,如果做操作,最好先判断是否为null。
*
* 开发中的原则:
* 只要是对象,在使用前就必须进行不为null的判断。
*/
public class IntegerDemo4 {
public static void main(String[] args) {
//创建一个包装类类型的对象
//Integer i = new Integer(100);
Integer ii = 100; //自动装箱
//public static Integer valueOf(int i)
ii += 200; //ii = ii + 200; 自动拆箱,自动装箱
//public int intValue()
System.out.println(ii);
//用工具反编译class文件后的底层代码
/*
Integer ii = Integer.valueOf(100);
ii = Integer.valueOf(ii.intValue() + 200);
System.out.println(ii);
*/
Integer iii = null;
if(iii != null) {
iii += 300; //NullPointerException
System.out.println(iii);
}
}
}
Date
/*
* 工具类
*
* 构造方法私有
* 成员方法静态
*/
public class DateUtil {
private DateUtil() {}
/*
* 把日期转换为指定格式的字符串
* 两个明确:
* 返回值类型:String
* 参数列表:Date date, String format
*/
public static String dateToString(Date date,String fomat){
SimpleDateFormat sdf = new SimpleDateFormat(fomat);
String s = sdf.format(date);
return s;
}
/*
* 把指定格式的字符串解析为日期
* 两个明确:
* 返回值类型:Date
* 参数列表:String s, String format
*/
public static Date stringToDate(String s,String format) throws ParseException{
SimpleDateFormat sdf = new SimpleDateFormat(format);
Date d = sdf.parse(s);
return d;
}
}
/*
* 使用今天的Date日期类定义程序 计算一个人出生了多少天
* 分析:
* Date时间类以1970-01-01 00:00:00 为基准
* 1计算生日与基准时间的时长birth
* 2计算当前时间与基准时间的时长now
* 3天数 = (now-birth)/1000/60/60/24
*
* SimpleDateFormat
* public Date parse(String source)
* public final String format(Date date)
*/
public class Demo {
public static void main(String[] args) throws ParseException {
//创建Date对象
Date now = new Date();;
System.out.println(now);
//时间格式化格式
String format = "yyyy-MM-dd HH:mm:ss";
//有参构造方式创建SimpleDateFormat对象
SimpleDateFormat sdf = new SimpleDateFormat(format);
/**
* Parses text from the beginning of the given string to produce a date.
* The method may not use the entire text of the given string.
* @param source A <code>String</code> whose beginning should be parsed.
* @return A <code>Date</code> parsed from the string.
* @exception ParseException if the beginning of the specified string
* cannot be parsed.
* public Date parse(String source) throws ParseException
*/
Date birth = sdf.parse("2019-03-04 00:00:00");
/**
* Formats a Date into a date/time string.
* @param date the time value to be formatted into a time string.
* @return the formatted time string.
* public final String format(Date date)
*/
String dateToString = sdf.format(now);
System.out.println(dateToString);
long day = (now.getTime()-birth.getTime())/1000/60/60/24;
System.out.println("已出生:"+day+"天");
}
}