abstract、static、构造方法

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);
	}

}

abstract、static、构造方法
②如果父类有参构造方法,其默认无参构造方法就会失效。如果父类只有有参构造方法,子类必须要有构造方法,无论有参或者无参,且子类要在构造方法内显示调用父类有参构造方法,这样才能确保子类初始化之前先初始化父类变量。

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类的抽线方法");
		
	}
	

}

abstract、static、构造方法
解决方法:
1,在子类构造方法中显示调用父类有参构造方法。该做法子类创建对象时会调用父类有参构造方法。
abstract、static、构造方法
输出:
abstract、static、构造方法

2,在父类中手动加入无参构造方法(推荐)。该做法在子类创建对象时会默认调用父类无参构造方法,即子类构造方法中自动调用super()函数。
abstract、static、构造方法
输出:
abstract、static、构造方法

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);
	}

}

abstract、static、构造方法
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);//子类有参构造方法中显示调用父类无参构造方法
	}

}

abstract、static、构造方法

  • ①父类是抽象类,子类是普通类,子类必须重写父类的抽象方法。
    ②父类是抽象类,子类也是抽象类,子类可以不重写父类的抽象方法,子类又可以被其他类继承。
    ③父类是普通类,子类是抽象类,也可以,子类不需要重写父类方法。

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的时候被分配内存空间。
    abstract、static、构造方法
  • 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();
	}
	

}

abstract、static、构造方法
③:静态代码块对于定义在它之后的静态变量可以赋值但不可以访问,所有这样做没意义。
abstract、static、构造方法

  • 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);
	}
	


}

abstract、static、构造方法
这里创建了一个home对象,p1和p2共享一个对象。如果把Home换成普通内部类类,那结果就变成:
abstract、static、构造方法
虽然也只创建了一个home对象,p1和p2共享该home对象,但该home对象是和p1共存亡的,一旦p1消亡,p2就不能使用了。但如果分别为p1和p2都创建一个home对象,这又浪费空间。所以在该例子中,静态内部类是最合适的选择。
类加载:java源码先编译成class文件,然后JVM把class文件中的类信息加载进内存中,并解析生成对应class文件,最后运行。JVM并不是一开始就把所有的类加载进内存中,而是只有第一次遇到某个需要运行的类才会加载,且只加载一次。
静态内部类加载:一个类的静态资源、常量(除字符串常量)在类被加载的时候就加载进内存分配空间,但静态内部类不是在类加载的时候被加载。外部类加载静态内部类不一定加载;静态内部类只在使用时加载,且一定先加载了外部类
①不能在非static内部类中再定义一个static内部类或者static方法、static属性,即只有在静态成员类或者是顶层类中才能生成静态成员变量、静态成员类和静态方法。
abstract、static、构造方法
解决方法:
abstract、static、构造方法
②static内部类可以不依赖于外部类实例对象而实例化,而普通内部类需要外部类对象实例化之后才能实例化。从某种意义上来说,static内部类已经不算严格意义上的内部类了,已经升级为*类。
abstract、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();
	}



}

abstract、static、构造方法

上一篇:Java——抽象类与抽象方法


下一篇:Java 关于抽象类匿名子类