例子:
class Animal
{
int num=4;
}
class Dog extends Animal
{
int num=5;
void show()
{
System.out.println(num);//此时结果为5,因为默认的是this.num
System.out.println(super.num);//结果为4,即调用的是父类中的变量。
}
}
class Test
{
public static void main(String[] args)
{
Dog dog=new Dog();
dog.show();
}
}
(2)super子类要调用父类构造函数时,可以用super语句。
例子:
class Animal
{
String name;
Animal(String name)
{
this.name = name;
}
}
class Dog extends Animal
{
Dog(String name)
{
super(name);//调用父类构造函数
}
}
(3)成员函数子类中出现与父类中一模一样的方法时,会出现覆盖操作,也成为重写或复写。
(1)覆盖的特点
1> 父类和子类中功能的定义一样,但是功能主体不一样。
2> 当子类对象调用该功能时,会运行子类中的此功能,如同父类的函数被覆盖一样。
3> 在子类覆盖方法中,若想使用父类被覆盖的方法,可以通过super.函数名来获取。
(2)子类函数覆盖父类函数的条件
1> 子类函数的权限必须大于等于父类函数的权限。
2> 父类中的私有方法和被final修饰的方法不可以被覆盖
3> 静态只能覆盖静态。
(3)由覆盖而产生的思想
当子类需要父类的功能,而功能主体中有子类特有的属性,可以复写父类的功能,这样沿袭了父类功能,又定义了子类特有内容。
(4)函数覆盖和函数重写的区别
函数重载:在同一个类中,存在多个同名函数,通过它们的参数列表(参数类型、参数个数、参数顺序)的不同来区分。
函数覆盖:在子类中出现和父类(函数名、参数列表、返回值类型)一模一样的函数
例子:
class Tel
{
void show()
{
System.out.println("number");
}
}
class NewTel extends Tel
{
void show()//复写了父类的相同功能
{
super.show();//调用父类的原有功能
System.out.println("name");//增加新的功能
System.out.println("pic");
}
}
class MethodTest
{
public static void main(String[] args)
{
NewTel nt=new NewTel();
nt.show();
}
}
(1)构造函数之间无覆盖功能。但是子类的构造函数会调用父类的构造函数,利用super语句子类构造函数之间也可以调用,通过this语句。
(2)子类的构造函数中至少有一个构造函数默认的第一行有一条隐式的语句super(),来调用父类的构造函数。
例子:
class Animal
{
Animal()
{System.out.println("Animal-----run");}}class Dog extends Animal{Dog(){//默认有super()System.out.println("Dog--run");}Dog(int num){this();//当子类构造函数的首句是this()语句时,则就没有默认的super()语句了System.out.println("Dog-----------num");}}class ConstructorTest {public static void main(String[] args) {Dog dog=new Dog();//最终结果为Animal-----run // Dog--run}}
(3)当父类中无空参数的构造函数时,则子类中必须手写上一句super(参数)语句,与父类中的构造函数一样,参数填父类构造函数参数的类型的值。
class Animal
{
Animal(int x)
{
System.out.println("Animal-----x");
}
}
class Dog extends Animal
{
Dog()
{
//由于父类无无参的构造函数,所以此处手冻的写上
super(3);
System.out.println("Zi--run");
}
Dog(int num)
{
this();//当子类构造函数的首句是this()语句时,则就没有默认的super()语句了
System.out.println("Zi-----------num");
}
}
class ConstructorTest
{
public static void main(String[] args)
{
Dog dog=new Dog();//最终结果为Animal-----3
// Dog--run
Dog dog1=new Dog(4);//结果为Animal-----3
//结果为Dog----------4
}
}
(5)总结
1> 类的所有的构造函数,默认都会访问父类中空参数的构造函数,因为子类每一个构造函数的首句都有一句隐式的super()语句。
2> 当父类中没有空参数的构造函数时,子类必须手动通过super语句形式来指定要访问父类中的构造函数。
3> 当然,子类中的构造函数第一行也可以手动指定this语句来访问本类中的构造函数。但是子类中至少有一个构造函数会访问父类中的构造函数。
二、抽象类
1.定义
多个对象都具备相同的功能,但是功能的具体内容不同,在向上抽取的过程中,只抽取功能定义,并未抽取功能内容。只有功能声明,没有功能主体的方法称为抽象方法。包含抽象方法的类就是抽象类。
2.抽象类的特点
1> 抽象类和抽象方法必须用abstract关键字修饰。
2> 抽象方法只有方法声明,没有方法体,定义在抽象类中。
3> 抽象类不可以实例化。
4> 抽象类通过其子类实例化。子类必须覆盖抽象类中的所有抽象方法才可以实例化对象,不然该子类也是抽象类。
5> 抽象类中可以有抽象方法,也可以没有,那么此抽象类只是为了不让创建它的对象。
3.抽象类和一般类的区别
抽象类和一般类没有太大区别,该怎么描述事物就怎么描述事物,只不过该事物出现了一些
看不懂的东西,这些不确定的部分,也是该事物的功能,需要明确出现,但是无法定主体,所以
通过抽象方法来表示。
4.由抽象类而总结出的编程思想:模板方法设计模式
在定义功能时,功能的一部分是确定的,但是有一部分是不确定的,而确定的部分在使用不确定的部分,
那么这时就将不确定的部分暴露出来,定义为抽象的,由该类的子类去完成。
例子:
/**
需求:获取一段程序的运行时间。
步骤:
1.定义一个获取时间的类
2.类中定义一个函数,用来获取时间
3.用一个函数单独用来记录运行的程序
模板方法设计模式:
定义:在定义功能时,功能的一部分是确定的,但是有一部分是不确定的,
而确定的部分在使用不确定的部分,那么这时就将不确定的部分暴露
出来(即抽象化),由该类的子类去完成(即在子类中将此方法进行复写)。
*/
abstract class GetTime
{
public void getTime()
{
long start=System.currentTimeMillis();
runCode();
long end=System.currentTimeMillis();
System.out.println("time="+(end-start));
}
//由于此程序并不确定,所以抽象出来
public abstract void runCode();
}
//此抽象方法由子类来具体实现
class SubTime extends GetTime
{
public void runCode()
{
for (int x=0;x<400 ;x++ )
{
if (x%2==0)
{
System.out.println(x);
}
}
}
}
class GetTimeDemo
{
public static void main(String[] args)
{
SubTime st=new SubTime();
st.getTime();
}
}
三、接口
interface 接口名
{}
2.接口中的成员修饰符是固定的
成员变量:public static final
成员函数:public abstract
注意:
接口中的成员都是public的
3.接口的特点
1> 接口是对外暴露的规则,是功能的扩展。
2> 接口可以用来多实现,即一个类可以实现多个接口。
3> 类和接口之间是实现关系,一个类可以继承一个类,并实现多个接口。
4> 接口和接口之间可以有继承关系,并且支持多继承。
5> 接口不可以创建对象,实现了接口的子类,必须覆盖接口中的所有方法才可以创建对象,否则该子类是抽象类。
6> 类实现接口用implements关键字。
例子:
interface A
{
public abstract void methodA();
}
interface B
{
public abstract void methodB();
}
class C
{
protected void methodC()
{
}
}
class D extends C implements A,B //实现多个接口时,中间用逗号隔开
{
@Override
public void methodA() //覆盖接口A中的方法
{
}
@Override
public void methodB() //覆盖接口B中的方法
{
}
}
1.单例设计模式
解决一个类在内存中只存在一个对象。
2.想要保证对象唯一
(1)为了避免其他程序过多的建立该类对象,先进制其他程序建立该类对象
(2)为了让其他程序访问到该类对象,只好在本类中定义一个对象
(3)为了方便其他程序对该对象的访问,可以对外提供一些访问方式
3.代码体现
(1)将构造函数私有化
(2)在类中创建一个本类对象
(3)提供一个获取到该对象的方法
4.饿汉式和懒汉式
//先初始化对象,称为饿汉式,开发会经常用饿汉式 ,因为安全
class Single
{
private static Single s = new Single();
private Single(){};
public static Single getInstance()
{
return s;
}
}
//需要时再初始化对象,称为懒汉式
//Single1进内存,对象还没有存在,只有调用了getInstance方法时,才会创建对象
//也称为对象的延时加载
class Single1
{
private static Single1 s1 = null;
private Single1(){};
public static Single1 getInstance()
{
if(s1 == null)
s1 = new Single1();
return s1;
}
}
//安全的懒汉式,同步代码块
class Single2
{
private static Single2 s2 = null;
private Single2(){};
public static Single2 getInstance()
{
if(s2 == null)
{
synchronized (Single2.class)
{
if(s2 == null)
s2 = new Single2();
}
}
return s2;
}
}