为什么不能用abstract修饰属性,私有方法,构造器,静态方法,final的方法?
abstract
抽象: 程序中不能被new创建的?父类,抽象,不完整,不具体,不能独立存在。通过 abstract 修饰类,意为抽象类,不能new对象
abstract: 抽象的,似是而非,像却又不是,具备某种对象的特征,但不完整
abstract修饰类概念: 不够完整,不够具体,不能独立存在
语法: abstract class 类名 { }
abstract修饰类:
- 抽象类,不能直接独立new对象;
- 可被子类继承,提供共性属性和方法;
- 可声明为引用,强制使用多态(更纯粹的多态);
- 抽象类的构造方法作用:构建子类对象时,先构建父类对象,(父类共性 + 子类独有 = 完整子类对象)
经验:abstract修饰后的抽象父类,依附于子类对象存在。
abstract修饰方法概念:抽象方法,不够完整、不够具体
语法:访问权限修饰符 abstract 返回值类型 函数名([参数列表]);
abstract修饰方法:
- 只有方法声明,没有方法实现;
- 必须包含在抽象类中(abstract class XX{});
- 强制子类必须实现该方法,提供完整的、具体的调用版本;
总结:
子类继承抽象类,子类必须覆盖父类中所有的抽象方法,否则子类还是抽象类;
抽象类中不一定有抽象方法,但有抽象方法的类一定是抽象类。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
| public class TestAbstract { public static void main(String[] args) { new NewDog(); OneAnimal a = new NewDog(); a.eat(); } } abstract class OneAnimal { String breed; int age; String sex; public OneAnimal() { System.out.println("OneAnimal()"); } public abstract void eat(); public void sleep() { System.out.println(); } } class NewDog extends OneAnimal { public NewDog() { System.out.println("Dog()"); } @Override public void eat() { System.out.println("狗在吃骨头..."); } }
|
abstract修饰类:
- 抽象类,不能直接独立new对象;
- 可被子类继承,提供共性属性和方法;
- 可声明为引用,强制使用多态(更纯粹的多态);
abstract修饰方法:
- 只有方法声明,没有方法实现;
- 必须包含在抽象类中(abstract class XX{});
- 强制子类必须实现该方法,提供完整的、具体的调用版本;
static
实例属性
,是每个对象各自持有的独立内存空间(多份),对象单方面修改,不会影响其他对象。
静态属性
,static 修饰的实例属性,为。静态属性,是整个类共同持有的共享空间(一份),任何对象修改,都会影响其他对象。
Java中规定:不能将方法体内的局部变量声明为 static!
静态属性:类属性
静态方法:类方法
不必创建对象,可直接通过类名访问【推荐】:类名.静态成员
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55
| public class TestStaticField { public static void main(String[] args) { MyClass mc1 = new MyClass(); mc1.a = 10; MyClass.b = 100; MyClass mc2 = new MyClass(); mc2.a = 20; MyClass.b = 200; System.out.println(mc1.a + " " + mc2.a); System.out.println(MyClass.b + " " + MyClass.b); System.out.println(); mc1.method2(); System.out.println(); mc2.method2(); } }
class MyClass { int a; static int b; static double PI = 3.14; public static void method1() { System.out.println("method1()..."); } public void method2() { System.out.println(MyClass.b); System.out.println(MyClass.PI); MyClass.method1(); } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| public class TestApplyStaticField { public static void main(String[] args) { Student[] stus = new Student[10]; for (int i = 0; i < stus.length; i++) { stus[i] = new Student(); } System.out.println("count最终值:" + Student.getCount()); } }
class Student { private static int count = 0; public Student() { count++; } public static int getCount() { return count; } }
|
如这几个常见的静态方法,均使用类名直接调用
- Arrays.copyOf();
- Arrays.sort();
- Math.random();
- Math.sqrt();
静态方法规则:
- 静态方法允许直接访问静态成员(不需要this.);
- 静态方法不允许直接访问非静态成员;
- 静态方法中不允许使用this或super关键字;
- 静态方法可以继承,不能重写(覆盖)、没有多态;
static技巧: 在执行类时,希望先执行的初始化动作,可以使用static定义一个静态代码区域,在类加载时即会被执行仅有的一次。
语法:
1 2 3 4 5 6
| public class example { static { } }
|
类加载
JVM首次使用某个类时,需通过CLASSPATH查找该类的.class文件
将.class文件中对类的描述信息加载到内存中,进行保存
如:包名、类名、父类、属性、方法、构造方法…
即:对一个类而言,加载的操作只做一次。
加载时机:
- 创建对象
- 创建子类对象
- 访问静态属性
- 调用静态方法
调用类加载语法: Class.forName("全限定名");
类加载时:
- 触发:静态属性和静态代码块的执行 - (仅1次)
- 顺序:静态属性初始化之后执行静态代码块
- 作用:可谓静态属性赋值,或必要的初始化行为
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91
| public class TestClassLoad { public static void main(String[] args) throws Exception { Class.forName("com.day16.t3_classloaded.Sub"); System.out.println(" ------------------------ "); new Sub(); System.out.println(" ************************ "); new Sub(); } }
class Super { static String field2 = "父类静态属性"; static { System.out.println(field2); System.out.println("父类静态代码块"); } String field1 = "父类实例属性"; { System.out.println(field1); System.out.println("父类动态代码块"); } public Super() { super(); System.out.println("父类无参构造方法"); } public static void method() { System.out.println("父类类中的静态方法"); } }
class Sub extends Super { static String subfield2 = "子类静态属性"; static { System.out.println(subfield2); System.out.println("子类静态代码块"); } String subfield1 = "子类实例属性"; { System.out.println(subfield1); System.out.println("子类动态代码块"); } public Sub() { super(); System.out.println("子类无参构造方法"); } public static void submethod() { System.out.println("子类类中的静态方法"); } }
|
static总结
(1)特点:
1、static是一个修饰符,static修饰的成员变量称之为静态变量或类变量。
2、static修饰的成员被所有的对象共享。
3、static优先于对象存在,因为static的成员随着类的加载就已经存在。
4、static修饰的成员多了一种调用方式,可以直接被类名所调用(类名.静态成员)。
5、static修饰的数据是共享数据,对象中的存储的是特有的数据。
(2)成员变量和静态变量的区别:
1、生命周期的不同: 成员变量随着对象的创建而存在随着对象的回收而释放。 静态变量随着类的加载而存在随着类的消失而消失。
2、调用方式不同: 成员变量只能被对象调用。 静态变量可以被对象调用,也可以用类名调用。(推荐用类名调用)
3、别名不同: 成员变量也称为实例变量。 静态变量称为类变量。
4、数据存储位置不同: 成员变量数据存储在堆内存的对象中,所以也叫对象的特有数据。 静态变量数据存储在方法区(共享数据区)的静态区,所以也叫对象的共享数据。
(3)静态使用时需要注意的事项:
1、静态方法只能访问静态成员(非静态既可以访问静态,又可以访问非静态)。
2、静态方法中不可以使用this或者super关键字。
3、主函数是静态的。
静态方法规则
:
- 静态方法允许直接访问静态成员(不需要this.);
- 静态方法不允许直接访问非静态成员;
- 静态方法中不允许使用this或super关键字(构造方法可以);
- 静态方法可以继承,不能重写(覆盖)、没有多态;
final
概念:最后的,不可更改的。—— 保护类/方法/变量的功能和值
final可修饰:
- 类(最终类):此类不能被继承,eg:String, Math, System均为final修饰的类
- 方法(最终方法):此方法不能被覆盖。
- 变量(最终变量):此变量值不能被改变(常量 - 通常变量名全大写),eg: Math.PI
实例常量赋值:显式初始化、动态代码块、构造方法
要求:
①DeadLine:在构造方法完成之前,为实例常量赋值即可;
②如果在构造方法中为实例常量赋值,必须保证所有的构造方法都能对其正确赋值。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
| public class TestFinal { public static void main(String[] args) { System.out.println(mc.num); final int[] nums = new int[]{11, 22, 33}; nums[0] = 88; } }
class MyClass { static final double numbers = 1.5;
static { } final int num = 10; { } public MyClass() { } public MyClass(int n) { } }
|
静态常量赋值:
显式初始化、静态代码块
要求:
①Deadline:在类加载完成之前,为静态常量赋值即可。
对象常量赋值:
final修饰基本类型:值不可变
final修饰引用类型:地址不可变
【总结】
final修饰类:此类不能被继承。
final修饰方法:此方法不能被覆盖。
final修饰变量:此变量值不可改变。(无初始值,只允许被赋值一次)
- 局部常量:显式初始化
- 实例常量:显式初始化、动态代码块、构造方法
- 静态常量:显式初始化、静态代码块
- 基本数据类型常量:值不可变。
- 引用数据类型常量:地址不可变。
final作为形参使用的原因:拷贝引用,为了避免引用值发生改变,例如被外部类的方法修改等,而导致内部类得到的值不一致,于是用final来让该引用不可改变。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85
| public class TestFinalArguments { public static void main(String[] args) { int a = 10; m1(a); System.out.println(a); final int[] nums = {11, 22, 33}; System.out.println(nums.length); m2(nums); System.out.println("m2()调用: " + nums.length); m3(nums); System.out.println("m3(): " + nums[0] + ", nums.hashCode()=" + nums.hashCode()); final Teacher[] t = {new Teacher(), new Teacher(), new Teacher()}; }
public static void m1(final int a) { System.out.println("形参a = " + a); }
public static void m2(int[] nums) { nums = java.util.Arrays.copyOf(nums, nums.length*2); System.out.println("m2()方法:" + nums.length); }
public static void m3(final int[] ns) { ns[0] = 88; System.out.println("ns.hashCode()=" + ns.hashCode()); }
public static void m4(final Teacher t1) { t1.name = "11"; }
public static Teacher[] m5(Teacher...teachers) { for (int i = 0; i < teachers.length; i++) { teachers[i].name += i; } return teachers; } } class Teacher { String name; final int age = 30; }
|