一、final
1.1 final修饰引用
- 如果引用是一个基本数据类型,则该引用为常量,值无法被修改
- 如果引用是一个引用数据类型,比如对象、数组。则该对象或者数组的内容可以修改,但指向该对象或数组的地址的引用不能修改
- 如果引用是类的成员变量,则必须当场赋值,否则编译会报错
public class test {
public static void main(String[] args) {
final int test1=1;
//此处赋值会报错
test1=10;
demo d=new demo();
d.s="2222";
//此处赋值会报错
d.j=10;
final int[] arr=new int[]{1,2,3,4,5};
arr[3]=999;
//此处赋值会报错
arr=new int[]{1,2,3};
}
}
final class demo{
String s="1111";
//此处没有当场赋值,编译会报错
final int i;
final int j=100;
}
1.2 final修饰方法
当使用final修饰方法时,这个方法无法被子类重写,但是可以被子类继承
1.3 final修饰类
当使用final修饰类时,该类成为最终类,无法被继承。
二、extends
2.1 继承的概念
继承是面向对象的一个显著的特征。继承是从已有的类中派生出新类,新的类能吸收已有类的属性和方法,并能拓展新的属性和方法。
继承的语法格式:
class 子类 extends 父类{}
如下是继承的一个例子:
package com.wfg.demo;
/**
* @Author WFG
* @Date 2019/6/1 19:45
*/
class Person{
private String name;
private String sex;
private int age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
class Student extends Person {
private String school;
public String getSchool() {
return school;
}
public void setSchool(String school) {
this.school = school;
}
}
public class TestDemo {
public static void main(String[]args){
Student stu1 = new Student();//实例化的是子类
stu1.setName("王富贵");//调用的是父类方法
stu1.setSex("你猜啊");//调用的是父类方法
stu1.setAge(18);//调用的是父类的方法
stu1.setSchool("某某大学");//子类自己的方法
System.out.println("姓名:"+stu1.getName()
+",性别:"+stu1.getSex()+",年龄:"+stu1.getAge()
+",学校:"+stu1.getSchool());
}
}
输出:
姓名:王富贵,性别:你猜啊,年龄:18,学校:某某大学
在上面的例子中,子类继承了父类的属性和方法,并对父类的方法进行了扩充。
2.2 继承的限制
虽然继承可以进行功能的扩充,但是其在定义的时候也是有若干限制的:
2.2.1 一个子类只能继承一个父类(单继承局限)
错误写法示例:
class A{}
class B{}
class C extends A,B{}
可以写成如下的多层继承:
class A{}
class B extends A{}
class C extends B{}
2.2.2 在一个子类继承的时候,实际上会继承父类中所有的属性和方法,但是要注意的是,对于所有的非私有操作(没有private)属于显式继承(可以直接利用对象操作),而所有的私有操作属于隐式继承(间接完成)。(或者直接理解为私有的属性和方法不能被继承)
package com.wfg.demo;
import javax.naming.Name;
/**
* @Author WFG
* @Date 2019/6/1 20:28
*/
class A {
private String name;
public void setName(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
class B extends A{
public void print() {
//错误,name属性是私有的(被private修饰),对外不可见
System.out.println(name);
}
}
public class TestDemo3 {
public static void main(String[]args){
B b = new B();
b.setName("王富贵");
System.out.println(b.getName());
}
}
虽然对A类中的name属性无法直接进行访问,但是却可以通过get、set方法间接进行操作。
2.2.3 在继承关系中,如果要实例化子类的对象,会默认的先调用父类的构造,为父类之中的属性初始化,之后再调用子类构造,为子类之中的属性初始化,即:默认情况下,子类会找到父类之中的无参构造方法。
例子:
package com.wfg.demo;
import javax.naming.Name;
/**
* @Author WFG
* @Date 2019/6/1 20:28
*/
class A {
public A() {
System.out.println("这是父类的无参构造");
}
}
class B extends A {
public B() {
System.out.println("这是子类的构造");
}
}
public class TestDemo3 {
public static void main(String[]args){
B b = new B();//实例化子类的对象
}
}
输出:
这是父类的无参构造
这是子类的构造
从上面可以看出,在实例化子类的对象时,调用了父类的默认构造方法,这对于子类而言,相当于隐含了一个super的形式:
class B extends A {
public B() {
super();//调用父类的构造
System.out.println("这是子类的构造");
}
}
以上例子都是调用的父类的无参构造,而如果这时候父类没有无参构造,则子类必须通过super()调用指定参数的构造方法:
package com.wfg.demo;
import javax.naming.Name;
/**
* @Author WFG
* @Date 2019/6/1 20:28
*/
class A {
public A(String name) {
System.out.println("这是父类的有参构造");
}
}
class B extends A {
public B() {
super("王富贵");//调用父类的构造
System.out.println("这是子类的构造");
}
}
public class TestDemo3 {
public static void main(String[]args){
B b = new B();//实例化子类的对象
}
}
运行结果:
这是父类的有参构造
这是子类的构造
三、native关键字
- 汇编生c,c生万物,其实java要实现对底层的控制,还是需要c/c++帮忙,老大毕竟是老大。
- native关键字我们开发应用的时候是用不到的,那什么时候用到呢?那些开发java语言的时候用到,native关键字是与c++联合开发的时候使用的,要不java控制不了底层啊,比如内存。所以还是那句:汇编生c,c生万物,c++是c的升级版。
- 这是java调用其他地方的接口的一个声明关键字,意思是这个方法不是java实现的,有挺多的编程语言都有这样的特性,比如c++里面使用extern "c"来表示告诉c++编译器去调用c里面已经实现好的函数,而不是自己去实现。native方法有点像java 里面的interface,都不用去实现,而是有别人去实现,但是interface是谁实现接口谁实现,native方法是直接交给c/c++来实现。java只能调用,由操作系统实现。
- native方法不能与abstract方法一起使用,因为native表示这些方法是有实现体的,但是abstract却表示这些方法是没有实现体的,那么两者矛盾,肯定也不能一起使用。
https://blog.csdn.net/Aphysia/article/details/80593654
四、static关键字
- static修饰的成员变量和方法,从属于类
- 普通变量和方法从属于对象
- 静态方法不能调用非静态成员,编译会报错
4.1 static关键字的用途
一句话描述就是:方便在没有创建对象的情况下进行调用(方法/变量)。
显然,被static关键字修饰的方法或者变量不需要依赖于对象来进行访问,只要类被加载了,就可以通过类名去进行访问。
static可以用来修饰类的成员方法、类的成员变量,另外也可以编写static代码块来优化程序性能
4.2 修饰成员变量
static变量也称为静态变量,静态变量和非静态变量的区别:
- 静态变量被所有对象共享,存放在JVM的方法区中,不是Java线程私有的内存区域
- 非静态变量是对象所拥有的,在创建对象的时候被初始化,存在多个副本,各个对象拥有的副本互不影响
- static成员变量初始化顺序按照定义的顺序来进行初始化
public class Person {
String name;
static int age;
public String toString() {
return "Name:" + name + ", Age:" + age;
}
public static void main(String[] args) {
Person p1 = new Person();
p1.name = "zhangsan";
p1.age = 10;
Person p2 = new Person();
p2.name = "lisi";
p2.age = 12;
System.out.println(p1);
System.out.println(p2);
}
/**输出结果
* Name:zhangsan, Age:12
* Name:lisi, Age:12
*/
}
可以看到age都为12,只保存了最后一次给age赋的值
给age属性加了static关键字后,Person对象就不再拥有age属性了,age属性会统一交给Person类去管理,即多个Person对象只会对应一个age属性
4.3 修饰成员方法
在类中使用static修饰的静态方法会随着类的定义而被分配和装载入内存中,而非静态方法属于对象的具体实例,只有在类的对象创建时在对象的内存中才有这个方法的代码段
由于静态方法不依赖于任何对象就可以直接访问,因此对于静态方法来说,是没有this的,因为不依附于任何对象,既然都没有对象,就谈不上this了,并且由于此特性,在静态方法中不能访问类的非静态成员变量和非静态方法: 因为静态方法和静态数据成员会随着类的定义而被分配和装载入内存中,而非静态方法和非静态数据成员只有在类的对象创建时在对象的内存中才有这个方法的代码段。(虽然在静态方法中不能访问非静态成员方法和非静态成员变量,但是在非静态成员方法中是可以访问静态成员方法和静态成员变量)
代码示例:
所以,如果想在不创建对象的情况下调用某个方法,就可以将这个方法设置为static。最常见的静态方法就是main方法,这就是为什么main方法是静态方法就一目了然了,因为程序在执行main方法的时候没有创建任何对象,只有通过类名来访问。
注意:
static方法是属于类的,非实例对象,在JVM加载类时,就已经存在内存中,不会被虚拟机GC回收掉,这样内存负荷会很大,但是非static方法会在运行完毕后被虚拟机GC掉,减轻内存压力
4.4 静态块(static{})
静态块用来优化程序性能,可以放置于类的任何地方,类中可以有多个静态块。
静态块在类被初次加载的时候会执行一次(仅执行这一次,这就是优化性能的原因),同时会按照static块的顺序来执行每个static块,一般用来初始化静态变量和调用静态方法。
下面的例子说明了静态块为什么能优化程序性能:
/**
* 每次调用isBornBoomer的时候
* 都会生成startDate和birthDate两个对象,造成了空间浪费
*/
class Person{
private Date birthDate;
public Person(Date birthDate) {
this.birthDate = birthDate;
}
boolean isBornBoomer() {
Date startDate = Date.valueOf("1997");
Date endDate = Date.valueOf("2019");
return birthDate.compareTo(startDate)>=0 && birthDate.compareTo(endDate) < 0;
}
}
/**
* 这里使用了static块
* 只需要进行一次的初始化操作
* 节省内存空间,优化性能
*/
class Person{
private Date birthDate;
private static Date startDate,endDate;
static{
startDate = Date.valueOf("1997");
endDate = Date.valueOf("2019");
}
public Person(Date birthDate) {
this.birthDate = birthDate;
}
boolean isBornBoomer() {
return birthDate.compareTo(startDate)>=0 && birthDate.compareTo(endDate) < 0;
}
}
因此,很多时候会将一些只需要进行一次的初始化操作都放在static代码块中进行。
注意:
- 静态初始化块可以置于类中的任何地方,类中可以有多个静态初始化块。
- 在类初次被加载时,会按照静态初始化块的顺序来执行每个块,并且只会执行一次。
五、public、protected、default、private
5.1 public
Java语言中访问限制最宽的修饰符,一般称之为”公共的“。被其修饰的类、属性以及方法不仅可以跨类访问,而且允许跨包访问。
5.2 private
Java语言中对访问权限限制的最窄的修饰符,一般称之为”私有的”。被其修饰的类、属性以及方法只能被该类的对象访问,其子类不能访问,更不能允许跨包访问
5.3 protected
介于public和private之间的一种访问修饰符,一般称之为保护型。被其修饰的类、属性以及方法只能被类本身的方法及子类访问,即使子类在不同的包也可以访问。
5.4 default
即不加任何访问修饰符,通常称为“默认访问模式”。该模式下,只允许在同一个包中进行访问。