1,abstract
- 抽象方法:用abstract修饰,只有方法名、返回值类型、形参,没有方法体,不能和static、private、final、native并列修饰一个方法。
- 抽象类中可以没有抽象方法,抽象类不能实例化对象(使用new关键字,由子类创建对象),多用于继承和实现(多态)。
- 有抽象方法的类一定是抽象类。
-
抽象类中可以有构造方法,帮助子类实例化,但构造方法不能用abstract修饰。
①:子类不能继承父类的构造方法,子类中所有的构造方法都会默认访问父类中的无参构造方法,原因:
1,子类会继承父类中的数据 并且可能使用父类中的数据。所以子类初始化之前 必须先完成父类数据的初始化。
2,每一个子类构造方法默认第一句都是super(),调用父类构造方法 并且括号中没有参数 说明调用的是无参构造方法
package Test;
//父类中有抽象方法 父类必须定义为抽象类
public abstract class Father {
//父类无参构造方法 构造方法不能用abstract修饰 只能用访问控制符public protect private修饰
public Father() {
System.out.println("fu类中无参构造方法");
}
//父类有参构造方法
public Father(int age) {
System.out.println("fu类中有参构造方法");
}
//父类普通方法 子类不用重写
public void outOrdinary() {
}
//父类抽象方法 子类如果不是抽象类的话必须重写
abstract public void outAbstract();
}
package Test;
public class Son extends Father{
//子类无参构造方法
public Son() {
//super();//使用super函数 可以指定访问fu中带参还是无参构造方法 默认访问无参构造方法 可以不写
System.out.println("zi类无参构造方法");
}
//子类有参构造方法
public Son(int age) {
//super();//使用super函数 可以指定访问fu中带参还是无参构造方法 默认访问无参构造方法 可以不写
System.out.println("zi类有参构造方法");
}
//子类不需要重写父类的具体方法 只需要重写父类的抽象方法
@Override
public void outAbstract() {
// TODO Auto-generated method stub
System.out.println("zi类重写了fu类的抽线方法");
}
}
package Test;
//测试子类和父类的构造方法
public class TestConstructor {
//主函数
public static void main(String[] args) {
//使用子类无参构造方法创建对象
Son son1 = new Son();
//使用子类有参构造方法创建对象
Son Son2 = new Son(20);
}
}
②如果父类有参构造方法,其默认无参构造方法就会失效。如果父类只有有参构造方法,子类必须要有构造方法,无论有参或者无参,且子类要在构造方法内显示调用父类有参构造方法,这样才能确保子类初始化之前先初始化父类变量。
package Test;
//父类中有抽象方法 父类必须定义为抽象类
public abstract class Father {
// //父类无参构造方法 构造方法不能用abstract修饰 只能用访问控制符public protect private修饰
// public Father() {
// System.out.println("fu类中无参构造方法");
// }
//父类有参构造方法
public Father(int age) {
System.out.println("fu类中有参构造方法");
}
//父类普通方法 子类不用重写
public void outOrdinary() {
}
//父类抽象方法 子类如果不是抽象类的话必须重写
abstract public void outAbstratc();
}
package Test;
public class Son extends Father{
//子类无参构造方法
public Son() {
//super();//使用super函数 可以指定访问fu中带参还是无参构造方法 默认访问无参构造方法
//super(20);//当将父类中没有无参构造方法时 可以访问有参构造方法
System.out.println("zi类无参构造方法");
}
//子类有参构造方法
public Son(int age) {
//super();
//super(20);//当将父类中没有无参构造方法时 可以访问有参构造方法
System.out.println("zi类有参构造方法");
}
//子类不需要重写父类的具体方法 只需要重写父类的抽象方法
@Override
public void outAbstratc() {
// TODO Auto-generated method stub
System.out.println("zi类重写了fu类的抽线方法");
}
}
解决方法:
1,在子类构造方法中显示调用父类有参构造方法。该做法子类创建对象时会调用父类有参构造方法。
输出:
2,在父类中手动加入无参构造方法(推荐)。该做法在子类创建对象时会默认调用父类无参构造方法,即子类构造方法中自动调用super()函数。
输出:
3,当子类中没有构造方法时,会默认使用子类的无参构造方法(无需写出,系统自带),从而也会访问父类的无参构造方法。
package Test;
//父类中有抽象方法 父类必须定义为抽象类
public abstract class Father {
//父类无参构造方法 构造方法不能用abstract修饰 只能用访问控制符public protect private修饰
public Father() {
System.out.println("fu类中无参构造方法");
}
//父类有参构造方法
public Father(int age) {
System.out.println("fu类中有参构造方法");
}
//父类普通方法 子类不用重写
public void outOrdinary() {
}
//父类抽象方法 子类如果不是抽象类的话必须重写
abstract public void outAbstratc();
}
package Test;
public class Son extends Father{
//子类不能继承父类的构造方法
//当子类中没有构造方法时 会默认访问父类的无参构造方法
//子类中所有的构造方法默认都会访问父类中的无参构造方法 先父再子 原因:
//1,子类会继承父类中的数据 并且可能使用父类中的数据。所以子类初始化之前 必须先完成父类数据的初始化
//2,每一个子类构造方法默认第一句都是super():调用父类构造方法 并且括号中没有参数 说明调用的是无参构造方法
//(如果父类中有有参构造方法的话 必须手动添加一个无参构造方法 否则只能super(参数)调用父类有参构造方法 子类构造方法会报错)
// //子类无参构造方法
// public Son() {
// //super();//使用super函数 可以指定访问fu中带参还是无参构造方法 默认访问无参构造方法
// //super(20);//!!!当将父类中没有无参构造方法时 可以访问有参构造方法
// System.out.println("zi类无参构造方法");
// }
//
// //子类有参构造方法
// public Son(int age) {
// //super();
// //super(20);//!!!当将父类中没有无参构造方法时 可以访问有参构造方法
// System.out.println("zi类有参构造方法");
// }
//子类不需要重写父类的具体方法 只需要重写父类的抽象方法
@Override
public void outAbstratc() {
// TODO Auto-generated method stub
System.out.println("zi类重写了fu类的抽线方法");
}
}
package Test;
//测试子类和父类的构造方法
public class TestConstructor {
//主函数
public static void main(String[] args) {
//使用子类无参构造方法创建对象
Son son1 = new Son();//如果自定义了构造方法(无论有参还是没参,默认无参构造方法就失效 如果还需使用无参构造方法 自己定义一个)
//使用子类有参构造方法创建对象
//Son Son2 = new Son(20);
}
}
4,子类可以在其构造方法中显示使用super函数,指定调用父类的构造方法。
package Test;
//父类中有抽象方法 父类必须定义为抽象类
public abstract class Father {
//父类无参构造方法 构造方法不能用abstract修饰 只能用访问控制符public protect private修饰
public Father() {
System.out.println("fu类中无参构造方法");
}
//父类有参构造方法
public Father(int age) {
System.out.println("fu类中有参构造方法");
}
//父类普通方法 子类不用重写
public void outOrdinary() {
}
//父类抽象方法 子类如果不是抽象类的话必须重写
abstract public void outAbstratc();
}
package Test;
public class Son extends Father{
//子类无参构造方法
public Son() {
//super();//使用super函数 可以指定访问fu中带参还是无参构造方法 默认访问无参构造方法
super(20);
System.out.println("zi类无参构造方法");
}
//子类有参构造方法
public Son(int age) {
super();//当父类中有无参构造函数时 如果没写super函数会自动调用父类的无参构造函数
//super(20);
System.out.println("zi类有参构造方法");
}
//子类不需要重写父类的具体方法 只需要重写父类的抽象方法
@Override
public void outAbstratc() {
// TODO Auto-generated method stub
System.out.println("zi类重写了fu类的抽线方法");
}
}
package Test;
//测试子类和父类的构造方法
public class TestConstructor {
//主函数
public static void main(String[] args) {
//使用子类无参构造方法创建对象
Son son1 = new Son();//子类无参构造方法中显示调用父类有参构造方法
//使用子类有参构造方法创建对象
Son Son2 = new Son(20);//子类有参构造方法中显示调用父类无参构造方法
}
}
- ①父类是抽象类,子类是普通类,子类必须重写父类的抽象方法。
②父类是抽象类,子类也是抽象类,子类可以不重写父类的抽象方法,子类又可以被其他类继承。
③父类是普通类,子类是抽象类,也可以,子类不需要重写父类方法。
2,static
静态的,被static修饰的资源不依赖于对象,直接通过类名.变量/方法就可以访问,生命周期和类相同,在类初始化的时候被加载,而非静态资源是类new的时候加载的,生命周期和对象一致。
- static和final的区别:
static int a=1;
final int b=1;
①:a在程序中可以被修改为其他值,但b不能修改;a可以通过类名.a直接调用,但b需要new该类的对象,然后通过对象.b调用。
②:a存放在静态空间,在程序运行过程中不会被释放,一直占着空间直到程序终止;b在程序用完它并且不会再用的时候就会被自动释放掉,不再占用内存。
- static修饰变量:static只能修饰成员变量,不能修饰局部变量,在类第一次通过类加载器加载到jvm的时候被分配内存空间。
- static修饰方法。static方法只能访问static变量和static方法,非static方法可以访问static变量和static方法,不需要创建对象就可以调用。static方法不可以使用this或者super等关键字,因为static已经属于类资源。
- static修饰代码块。
①:static代码块常用于类的初始化操作,只执行一次,以优化程序性能。
package Test;
import java.sql.Date;
public class TestStatic {
private Date birthDate;//某个人的出生日期
boolean flag;//判断该人是否在某个年限区间内出生
//构造方法 将外界出生日期传入
public TestStatic(Date birthDate) {
this.birthDate = birthDate;
}
public boolean isBabyBoom() {
Date startDate = Date.valueOf("1900");//出生开始日期
Date endDate = Date.valueOf("1999");//出生结束日期
flag = birthDate.compareTo(startDate)>=0 && birthDate.compareTo(endDate)<0;//判断一个人是否是1900~1999年出生
return flag;
}
}
isBabyBoom()方法用来判断一个人是否在1900~1999年出生,每次调用该方法的时候都会生成两个变量startDate和endDate,造成空间浪费,因此可以做下列优化:
package Test;
import java.sql.Date;
public class TestStatic {
private Date birthDate;//某个人的出生日期
boolean flag;
static Date startDate;
static Date enDate;
//构造方法 将外界出生日期传入
public TestStatic(Date birthDate) {
this.birthDate = birthDate;
}
static {
startDate = Date.valueOf("1900");//出生开始日期
enDate = Date.valueOf("1999");//出生结束日期
}
public boolean isBabyBoom() {
flag = birthDate.compareTo(startDate)>=0 && birthDate.compareTo(enDate)<0;//判断一个人是否是1900~1999年出生
return flag;
}
}
将一些只需要进行一次的初始化操作放在static代码块中进行。
②静态代码块执行顺序:父静态代码块–>子静态代码块,静态代码块–>实例化对象,且按照定义顺序加载,只执行一次。
package Test;
//父类中有抽象方法 父类必须定义为抽象类
public class Father {
static {
System.out.println("fu的静态代码块");
}
//父类的构造方法
public Father() {
System.out.println("fu的构造方法");
}
}
package Test;
public class Son extends Father{
static {
System.out.println("zi的静态代码块");
}
//子类的构造方法
public Son() {
System.out.println("zi的构造方法");
}
//主函数 程序入口
public static void main(String[] args) {
new Son();
new Son();
}
}
③:静态代码块对于定义在它之后的静态变量可以赋值但不可以访问,所有这样做没意义。
- static修饰类:当外部类需要使用内部类,而内部类无需使用内部类资源的时候使用。
package Test;
//说明静态内部类的优势
public class PersonStatic {
private String name;//姓名
private Home home;//家庭
//外部类的构造方法
public PersonStatic(String name) {
this.name = name;
}
//私有属性name Home的set和get方法
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Home getHome() {
return home;
}
public void setHome(Home home) {
this.home = home;
}
//静态内部类 外部类需要使用内部类属性 但内部类不需使用内部类属性 只需加载一次 类的对象都可以共享一个静态内部类的属性
public static class Home{
private String addr;//家庭住址
private String tel;//家庭电话
//静态内部类的构造方法
public Home(String addr,String tel) {
this.addr = addr;
this.tel = tel;
}
//私有属性addr tel的set和get方法
public String getAddr() {
return addr;
}
public void setAddr(String addr) {
this.addr = addr;
}
public String getTel() {
return tel;
}
public void setTel(String tel) {
this.tel = tel;
}
}
//主函数 程序入口
public static void main(String[] args) {
//生成静态内部类对象
PersonStatic.Home home = new PersonStatic.Home("上海", "010");
//生成两个外部类对象
PersonStatic p1 = new PersonStatic("小明");
PersonStatic p2 = new PersonStatic("小华");
p1.setHome(home);
p2.setHome(home);
}
}
这里创建了一个home对象,p1和p2共享一个对象。如果把Home换成普通内部类类,那结果就变成:
虽然也只创建了一个home对象,p1和p2共享该home对象,但该home对象是和p1共存亡的,一旦p1消亡,p2就不能使用了。但如果分别为p1和p2都创建一个home对象,这又浪费空间。所以在该例子中,静态内部类是最合适的选择。
类加载:java源码先编译成class文件,然后JVM把class文件中的类信息加载进内存中,并解析生成对应class文件,最后运行。JVM并不是一开始就把所有的类加载进内存中,而是只有第一次遇到某个需要运行的类才会加载,且只加载一次。
静态内部类加载:一个类的静态资源、常量(除字符串常量)在类被加载的时候就加载进内存分配空间,但静态内部类不是在类加载的时候被加载。外部类加载静态内部类不一定加载;静态内部类只在使用时加载,且一定先加载了外部类。
①不能在非static内部类中再定义一个static内部类或者static方法、static属性,即只有在静态成员类或者是顶层类中才能生成静态成员变量、静态成员类和静态方法。
解决方法:
②static内部类可以不依赖于外部类实例对象而实例化,而普通内部类需要外部类对象实例化之后才能实例化。从某种意义上来说,static内部类已经不算严格意义上的内部类了,已经升级为*类。
package Test;
public class TestStatic2 {
static class staticInner{
public void staticOut() {
System.out.println("这是static内部类的方法");
}
}
class ordinaryInner{
public void ordinaryOut() {
System.out.println("这是普通内部类的方法");
}
}
public static void main(String[] args) {
//创建static内部类 并调用其方法
TestStatic2.staticInner staticinner = new TestStatic2.staticInner();
staticinner.staticOut();
//创建普通内部类 并调用其方法
TestStatic2.ordinaryInner ordinaryinner = new TestStatic2().new ordinaryInner();
ordinaryinner.ordinaryOut();
}
}