继承基础使用
class Person{}
class Student extends Person{}
子类可以通过继承,直接把父类中的方法拿到子类中使用,也可以定义自己的属性和方法
Java中只允许单继承,即一个子类一次只能继承一个父类
不过Java支持多层继承,不能多重继承
C extends B, B extends A √
C extends B, A ×
父类又称为基类
子类又称为派生类
继承,更准确的含义是扩展,子类不仅可以保留父类原有的功能,还可以拥有更多的功能
在使用继承的时候,需要注意的是,子类无法直接访问父类中的私有成员
注意,子类对象的实例化,会先调用父类中的构造方法,再调用子类自己的构造方法
之所以先调用父类中的构造方法,就是要用父类的构造方法为父类中的属性初始化,表示先有父类实例,然后才能产生子类实例,实际上在子类的构造方法中隐含了一个super()的语法
方法的重写与重载
-
方法重载 Overloading
1)方法名称相同,参数的类型和个数不同
2)对重载方法和原方法的权限没有要求
3)发生在一个类中
-
方法重写 Overriding
1)方法名称、参数的类型、返回类型全部相同,方法体不同
2)被重写的方法权限扩大
3)发生在父子类中
super与this关键字
在子类中使用super.属性的形式访问父类中的属性,super.方法()的形式访问父类中的方法
-
super
1)访问父类中的属性、方法
2)调用父类的构造方法时,必须放在子类构造方法的首行(因为构造方法优先调用)
-
this
1)访问本类中的属性、方法如果本类中没有该属性或方法,则会从父类中继续查找
2)调用本类构造方法时,必须放在构造方法的首行(因为构造方法优先调用)
注意,this和super都可以调用构造方法,但是两者是不能同时使用,因为两者在调用构造方法时都必须放在首行
final关键字(终结器)
1)使用final声明的类不能有子类
2)使用final声明的方法不能被子类所重写
3)使用final声明的变量即成为常量,常量不可以修改
class A{
private final String str = "AAA";
str = "BBB";// 此处会报错 Cannot assign a value to final variable 'str'
}
另外,如果使用public static final声明一个变量,则此变量称为全局常量(公共、静态、不可改变)
抽象类 abstract
抽象类的作用是用作模板,其中的抽象方法由子类去实现
1)抽象类中至少有一个抽象方法
2)抽象类和抽象方法都要使用abstract关键字声明
3)抽象方法只用声明不需要实现,交给子类重写实现
4)抽象类必须被子类继承,子类如果不是抽象类,必须重写抽象类中全部的抽象方法
abstrcat class A{
public abstract String getInfo();
}
// 抽象类只能被继承
class B extends A{
@override
public String getInfo(){
}
}
可以这么理解,抽象类就是比普通方法多了一些抽象方法,且不能直接对象实例化,其他部分是一样的
注意,抽象类不能使用final关键字,因为抽象类必须由子类继承,由子类去重写其中的抽象方法
注意,抽象方法不要用private来修饰,否则子类无法重写
另外,抽象类中其实可以存在构造方法,用来初始化抽象类的属性,虽然抽象方法不能被实例化,但是子类在实例化时依旧会先对父类进行实例化
抽象内部类
如果抽象类的子类只有一个时,可以将它封装到抽象类的内部类中,从而隐藏这个无需知道的类,这在开发时很常用
abstract class A //抽象类
{
// abstract修饰的方法为抽象方法,无需实现方法体.但是必须被子类覆写
abstract public void printData();
// 需要在抽象类里隐藏的内部类
static private class B extends A{
private int Data=12;
public void printData(){
System.out.println("B Data:"+this.Data); //打印内部类的Data值
}
}
// 获取实例对象
static public A getInstance(){
//通过静态方法来获取要隐藏的静态抽象内部类 (静态方法里只能使用静态变量)
return new B();
}
}
public class Test{
public static void main(String args[]){
A a = A.getInstance();
a.printData(); //等价于: A.getInstance().printData();
}
}
接口类 interface
接口属于一种特殊的类,这个类里面只允许有抽象方法和全局常量public static final(该限制在JDK1.8之后被打破)
接口中也可以定义普通方法和静态方法
接口实际上只是表示一种操作标准,接口本身并没有操作能力,需要子类去实现这个操作能力
接口的几个原则
1)接口通过interface关键字来定义
2)一个子类如果需要实现接口的话,需要通过implements关键字,从而实现多继承
3)接口的子类如果不是个抽象类,则必须重写接口中的所有抽象方法
4)接口的子对象,可以通过向上转型进行实例化操作
5)规定接口由抽象方法和全局常量组成,所有在接口中方法和常量不写,默认也是abstract和public
interface A{
public static final String HA = "hello";
public abstract String getInfo();
}
// 开发时经常简写,但是仍然是全局常量和公共的抽象方法
interface A{
String HA = "hello";
String getInfo();
}
// 接口可以被实现可以被继承
class B implements A{
String getInfo(){
System.out.println("Hello");
}
}
Java中限制单继承,但是可以多实现
class C implements A,B
如果一个类既要实现接口又要继承父类,则写法如下,先继承后实现
class C extends B implements A
另外,接口也可以继承
interface B
interface C extend B
多态
多态是面向对象中最重要的概念
主要有2种体现方式:
1、方法的多态性:重载、重写
2、对象的多态性:向上转型、向下转型
下面主要介绍对象的多态性
-
向上转型(自动完成)
-
向下转型(需要明确指出要转型的子类类型)
对象的多态(上下转型)
父子对象之间的转换分为了向上转型和向下转型
区别在于
向上转型,通过子类对象实例化父类对象,这种属于自动转换
向下转型,通过父类对象实例化子类对象,这种属于强制转换
向上转型
向上转型时,父类对象只能调用父类方法或者被子类重写后的方法,无法调用子类中单独的方法
class A {
public void print() {
System.out.println("A:print");
}
}
class B extends A {
public void print() {
System.out.println("B:print");
}
}
public class Test{
public static void main(String args[])
{
A a = new B(); //通过子类去实例化父类
a.print();
}
}
这里的a.print()调用的是子类B中的重写方法
因为我们通过子类B去实例化的,所以父类A中的print方法被子类B中的print重写了
这样做的意义是
当我们需要相同父类的多个对象调用某个方法时,通过向上转换后,则可以确定参数的统一
class A {
public void print() {
System.out.println("A:print");
}
}
class B extends A {
public void print() {
System.out.println("B:print");
}
}
class C extends B {
public void print() {
System.out.println("C:print");
}
}
public class Test{
// 因为向上转型,这里方法参数可以统一为父类类型
public static void func(A a)
{
a.print();
}
public static void main(String args[])
{
func(new B()); //等价于 A a =new B();
func(new C()); //等价于 A a =new C();
}
}
输出结果
B:print
C:print
但是注意,两种不同的对象有相同的父类,作为统一的参数
向下转型
向下转型是为了通过父类强制转换为子类,从而来调用子类独有的方法(向下转型一般不会用)
class A {
public void print() {
System.out.println("A:print");
}
}
class B extends A {
public void print() {
System.out.println("B:print");
}
public void talk(){
System.out.println("B:talk");
}
}
public class Test{
public static void main(String args[])
{
A a = new A();
B b = (B)a; // 向下转型
b.talk(); // 向上转型,调用子类独有的方法
}
}
输出结果
B:talk
instanceof关键字
instanceof用于判断一个对象到底属于哪个类的实例,返回boolean
A a = new B(); // 向上转型 (B类是A的子类)
a instanceof A; // true
a instanceof B; // true
a instanceof C; // false
工厂设计模式(3种)
1、简单工厂
- 实现类的共同接口
- 具体类的实现类
- 工厂类的实现
2、工厂方法
- 实现类的共同接口
- 具体的实现类
- 抽象工厂(工厂的共有接口)
- 具体工厂
3、抽象工厂(了解)
Object 类的9个常用方法,共12个
Object是Java中所有类的基类,任何类都默认继承Object,其提供的方法主要有:
1、registerNatives() 向JVM注册native方法,仅了解
private static native void registerNatives();
static {
registerNatives();
}
2、getClass() 反射的一种方式
public final native Class<?> getClass();
类加载的第一阶段是将.class文件加载到内存中,并生成一个java.lang.Class对象的过程
getClass()方法就是获取这个对象,这是当前类的对象在运行时类的所有信息的集合
反射共有3种方式
- 对象的getClass()
- 类名.class
- Class.forName()
3、 hashCode() 返回当前对象的hashCode值
public native int hashCode();
4、 equals() 比较对象的堆地址,String类中重写equals用于比较内容值
public boolean equals(Object obj) {
return (this == obj);
}
5、 clone() 此方法返回当前对象的一个副本(浅拷贝),使用前需要实现Cloneable接口
protected native Object clone() throws CloneNotSupportedException;
浅拷贝,拷贝的是引用
深拷贝,新开辟内存空间,进行值拷贝
6、toString() 默认输出当前类的全限定类名+@+十六进制的hashCode值(建议所有子类都重写toString方法,返回值为String字符串)
public String toString() {
return getClass().getName() + "@" + Integer.toHexString(hashCode());
}
7、wait(long time) 阻塞当前线程,等待其他线程调用notify()将其唤醒(public final不可重写的方法)
public final native void wait(long timeout) throws InterruptedException;
public final void wait(long timeout, int nanos) throws InterruptedException {
if (timeout < 0) {
throw new IllegalArgumentException("timeout value is negative");
}
if (nanos < 0 || nanos > 999999) {
throw new IllegalArgumentException(
"nanosecond timeout value out of range");
}
if (nanos > 0) {
timeout++;
}
wait(timeout);
}
public final void wait() throws InterruptedException {
wait(0);
}
wait()、wait(long)、wait(long, int) 重载方法
8、notify()、notifyAll() 唤醒线程
public final native void notify();
public final native void notifyAll();
9、finalize() 此方法是在GC垃圾回收前,JVM会调用此方法来清理资源
protected void finalize() throws Throwable { }
包装类以及包装类的应用
Java中的数据类型分为基本数据类型和引用数据类型
但是基本数据类型如何成为一个对象呢
为解决这个问题,需要将这8种基本类型包装成一个类的形式,即包装类
byte -- Byte
int -- Integer
float -- Float
double -- Double
short -- Short
long -- Long
char -- Character
boolean -- Boolean
上述的包装类中,其中Byte、Integer、Float、Double、Short、Long都属于Number类的子类
Number类本身提供一系列返回上述6种基本数据类型的操作
Character和Boolean属于Object的直接子类
装箱与拆箱
装箱:将一个基本数据类型变为包装类
拆箱:将一个包装类变为基本数据类型
装箱
Integer a = 5;
Integer a = Integer.valueOf(5);
拆箱
Integer b = new Integer(6);
int c = b.intValue();
valueOf() 和 xxxValue() 方法均来自于Number类
在JDK1.5之后,装箱和拆箱都自动实现
String类与Integer、Float类型互相转换
1、 String类转为Integer、Float类
在Integer和Float类中提供方法parseXX()
public static int parseInt(String s) throws NumberFormatException {
return parseInt(s,10);
}
public static float parseFloat(String s) throws NumberFormatException {
return FloatingDecimal.parseFloat(s);
}
String str1 = "111";
int a = Integer.parseInt(str1);
String str2 = "11.1";
float b = Float.parseFloat(str2);
2、 Integer、Float类转为String类
int a = 11;
float b = 11.1;
String str3 = String.valueOf(a);
String str4 = String.valueOf(b);
注意这种转换,要求字符串必须是数字类型,否则转换报错
匿名内部类
就是没有名字的内部类
优点:不用创建子类
缺点:只能使用一次,若要多次使用还是老老实实创建子类
使用条件:必须存在继承或实现关系的时候才会使用,且匿名内部类没有名字,只能使用一次,必须重写父类或接口中的方法
作用:实现接口或重写父类方法,如果只用一次就不需要再写子类了
只能通过继承它的父类或实现一个接口来达到这一目的
public class AnnoInner {
public static void main(String[] args) {
// 匿名内部类重写父类方法
new Animals(){
public void eat(){
System.out.println("我是匿名内部类");
}
}.eat();
// 匿名内部类实现接口方法
new Car(){
public void price(){
System.out.println("12万");
}
}.price();
}
}
class Animals {
public void eat() {
System.out.println("动物也要吃饭");
}
}
interface Car{
public void price();
}
匿名内部类最常用的是作为实参传递
public class AnnoInner2 {
public static void main(String[] args) {
// 将匿名内部类作为参数传递
say(new P(){
public void talk(){
System.out.println("不说话了");
}
});
}
public static void say(P p){
p.talk();
}
}
class P{
public void talk(){
System.out.println("人在说话");
}
}
匿名内部类常见格式
new 父类构造器(参数列表)|实现接口()
{
//匿名内部类的类体部分
}
在使用匿名内部类的过程中,我们需要注意如下几点:
1、使用匿名内部类时,我们必须是继承一个类或者实现一个接口,但是两者不可兼得,同时也只能继承一个类或者实现一个接口。
2、匿名内部类中是不能定义构造函数的。
3、匿名内部类中不能存在任何的静态成员变量和静态方法。
4、匿名内部类为局部内部类,所以局部内部类的所有限制同样对匿名内部类生效。
5、匿名内部类不能是抽象的,它必须要实现继承的类或者实现的接口的所有抽象方法。
问题
重写与重载区别
方法重载 Overloading
1)方法名称相同,参数的类型和个数不同
2)对重载方法和原方法的权限没有要求
3)发生在一个类中
方法重写 Overriding
1)方法名称、参数的类型、返回类型全部相同,方法体不同
2)被重写的方法权限扩大
3)发生在父子类中
this与super区别
this
1)指向当前对象本身
2)形参与成员名字重名,用this来区分
public Person(String name, int age) {
this.name = name;
this.age = age;
}
3)引用本类的构造方法
class Person{
private String name;
private int age;
public Person() {
}
public Person(String name) {
this.name = name;
}
public Person(String name, int age) {
this(name);
this.age = age;
}
}
super
super可以理解为指向自己父类对象的一个指针
1)指向当前对象的父类的引用(与this相似)
2)子类的成员变量和方法与父类中的同名时,用super区分
class Person{
protected String name;
public Person(String name) {
this.name = name;
}
}
class Student extends Person{
private String name;
public Student(String name, String name1) {
super(name);
this.name = name1;
}
public void getInfo(){
System.out.println(this.name); //Child
System.out.println(super.name); //Father
}
}
3)引用父类的构造方法
super 调用父类中的某一个构造方法,一般是无参构造方法
this与super的区分:
1)this和super不能出现在同一个构造方法中
2)this()和super()必须放在构造方法的第一行
3)this和super类似,super()是调用父类构造方法,this()是调用本类自己的构造方法
抽象类与接口的区别
接口是一种行为约束,只能控制行为的有无,但是对如何实现没有限制
抽象是一种模板,为了代码复用,当不同的类有很多相同的成员
向上转型、向下转型
向上转型,子类实例化父类对象,自动转换,只能调用父类中被子类重写的方法,而子类中的单独方法则是无法调用的
作用:统一参数,当一个方法的参数为父类对象时,其子类对象都可以作为参数
向下转型,父类强制转换为子类,只能调用子类独有的方法(实际开发很少使用)
native
native是与C++联合开发的时候用的!java自己开发不用的!