abstract
abstract,译为抽象的。意思就是不能具体化。在java中,abstract常常用于修饰类或方法。
- 修饰类,表示该类不能new一个对象,不能调用构造函数实例化一个对象。修饰类就是为了子类来继承该类。(也就是不能写 Father f = new Father() ;来创建对象。)
- 修饰方法时,表示该方法后面没有{}。举个栗子:public abstract void func() ;就只能这样写,表示一个公有的,无返回值的,方法名叫func的抽象方法。就是父类不能够创建对象实现该方法,只有子类重写该方法。同时很重要的一点,写了抽象方法,那么该类前面就要写abstract修饰。子类继承该类时,要么子类也是抽象类,也就是abstract修饰该类;要么就是重写父类的抽象方法。修饰方法就是为了子类重写该方法,如果用private修饰该方法,该方法就不能被子类继承,那怎么重写。
抽象方法不能定义为private
几个误区
- 抽象类里不一定有抽象方法,但是抽象方法一定是抽象类。
- 抽象类中也可以与普通类一样定义属性,方法等。
举个栗子:抽象类
先写个父类,是抽象类
/**
* 创建抽象父类
* @author Mould
*
*/
public abstract class Father {
/**
* 抽象类里面可以创建实例属性和方法
*/
String name ; //这个就是实例属性
/**
*
* @param name传入的实例属性
* @return 传入的属性,就是为了获得输出
*/
public String getName(Father f) {
return name ;
}
}
再写个子类,继承父类这个抽象类。
public class Son extends Father{
//子类为空
}
再写个main方法来创建对象。
public class Demo {
public static void main(String[] args) {
//先来看看Father是一个抽象类,我们看看能不能new一个对象
Father father = new Father(); //这行代码会报错,因为父类是抽象类,不能创建对象
//再来看看继承父类抽象类的子类能不能new一个对象
Son s = new Son(); //子类可以创建对象
s.name = "张三" ;
System.out.println(s.getName(s) );
}
}
结果很明晰,抽象类不能创建对象
抽象类的子类可以创建对象
再举个栗子:抽象方法
有个抽象类叫做父类。
public abstract class Father {
private int num ;
private String name ;
public int getNum() {
return num;
}
public void setNum(int num) {
this.num = num;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public abstract void func1() ;//这是一个抽象方法,那么这个类前面也会有abstract修饰。
}
再写一个继承父类的子类。
//这是最常见子类的写法
public class Son extends Father{
public Son(int num, String name) {
super();
this.setNum(num);
this.setName(name);
}
//因为父类中有抽象方法,所以子类需要重写父类里面的抽象类
@Override
public void func1() {
System.out.println("子类继承有抽象方法的父类,子类需要重写父类中抽象方法,子类该方法前不需要写abstract");
}
}
第二种方法写子类方法,将子类定义为抽象类,这样就没有什么意义了。不推荐该写法
public abstract class Son extends Father{
}
再写个main方法调用一下。
public class Demo {
public static void main(String[] args) {
Son s = new Son(123 , "张三");
System.out.println(s.getName() + " " + s.getNum());
s.setName("李四");
System.out.println(s.getName() );
s.setNum(456);
System.out.println(s.getNum());
}
}
看看结果
static
一般将它说为静态的,但是比较好理解的是类的。一般是修饰属性和方法。
- 修饰属性,表示该方法是静态的。称为类属性。类属性是整个类所有对象共同拥有的属性,静态属性直接通过类名.静态属性名来赋值
举个栗子
public class Static1 {
static String name ;
private String num ;
public void funcNoraml2() {
//这是一个实例方法
//这是一个实例方法
}
public void funcNoraml3() {
//这是一个实例方法,可以调用类属性,类方法,实例属性,实例方法
System.out.println(name);
System.out.println(num);
funcNoraml2();
funcStatic2();
funcStatic3();
}
public static void funcStatic2() {
//这是一个静态方法
}
public static void funcStatic3() {
//这是一个静态方法,只能调用类属性和类方法
System.out.println(name);
System.out.println(num); //这行报错,不能调用实例属性
funcNoraml2(); //这行报错,不能调用实例方法
funcNoraml3(); //这行报错,不能调用实例方法
funcStatic2();
}
}
实例方法可以调用类中静态属性,静态方法,实例属性,实例方法。一切都可以调用
静态方法只能调用静态属性,静态方法
看下父类和子类的静态属性和方法以及实例属性和方法在内存中加载的过程
先看父类
public class Father {
static String fatherName = "父类的静态属性";
String num = "父类的实例属性";
//动态代码块,创建对象执行
{
System.out.println(num);
System.out.println("父类的动态代码块的执行");
}
static {
System.out.println(fatherName);
System.out.println("父类的静态代码块的执行");
}
public void funcNoraml2() {
//这是一个实例方法
//这是一个实例方法
System.out.println("父类的实例方法。。。");
}
public static void funcStatic2() {
//这是一个静态方法
System.out.println("父类的静态方法。。。");
}
public Father() {
System.out.println("父类的构造方法。。。");
}
}
再看子类
public class Son extends Father {
static String sonName = "子类的静态属性";
String num = "子类的实例属性";
//动态代码块,创建对象执行
{
System.out.println(num);
System.out.println("子类的动态代码块的执行");
}
static {
System.out.println(sonName);
System.out.println("子类的静态代码块的执行");
}
public void funcNoraml2() {
//这是一个实例方法
//这是一个实例方法
System.out.println("子类的实例方法。。。");
}
public static void funcStatic2() {
//这是一个静态方法
System.out.println("子类的静态方法。。。");
}
public Son() {
super();
System.out.println("子类的构造方法。。。");
}
}
再写个main方法调用
public class Demo1 {
public static void main(String[] args) {
Son s = new Son() ;
}
}
看看运行结果
为什么会出现这种情况,就要讲讲类加载和创建对象的过程。直接说结论,因为过程我无法说清楚。先类加载(同时有static修饰的属性和方法也会加载进内存并且一直保存在内存中,除非程序结束),后创建对象(实例属性和实例方法在这个过程中加载进内存)。
final
final表示最终的,可以修饰类,属性以及方法,
先来看看final修饰类能不能被继承。
一个final修饰的父类
/**
* final修饰类
* @author Mould
*
*/
public final class Father {
}
Son来继承final修饰的子类
/**
* 搞个子类来继承一下最终类,看看什么结果。
* @author Mould
*
*/
public class Son extends Father {
//这行报错
}
看看final修饰方法
/**
* final修饰方法
* @author Mould
*
*/
public class Father {
final void func() {
System.out.println("父类的final修饰的方法");
}
}
/**
* 搞个子类来继承一下类,重写一下final修饰的方法。
* @author Mould
*
*/
public class Son extends Father {
@Override
void func() {
//报错了
}
}
看看final修饰属性。一般就就认为是常量,我们一般把常量字母全部大写,通过下划线连接单词,例如:MAX_VALUE。
先看看父类
/**
* final修饰属性
* @author Mould
*
*/
public class Father {
final int num ;//这行报错了
}
正确方法
public class Father {
//第一种赋值方法
final int num = 1;
//第二种赋值方法,在构造函数中给final修饰的变量·
final String name ;
public Father() {
name = "张三" ;
}
}
final修饰的属性可以直接赋初值,也可以在构造方法中赋初值。
有一个属性如果同时static final修饰,会怎么样。
上结论
//一个变量同时被static,以及final修饰的话,那么就要定义时赋初值,不然会报错
static final int no = 15 ;
一个变量同时被static,以及final修饰的话,那么就要定义时赋初值,不然会报错
一个数组或者对象前面加final修饰会怎么样呢。
public class Father {
final int [] num = {
1 , 2};
public Father() {
num[0] = 15 ;
}
}
final修饰的数组可以修改数组中的值,因为,final修饰的数组是数组名里保存的地址固定了,但是如果像下面这样写就会报错。
下面这是错误范例
public class Father {
final int [] num = {
1 , 2};
public Father() {
num[0] = 15 ;
}
num = new [5];//这行会报错,因为new又会开辟新空间,那么地址就变了。
}