封装的目的是简化编程和加强安全性。java
简化编程是指,封装可让使用者没必要了解具体类的内部实现细节,而只是要经过提供给外部访问的方法来访问类中的属性和方法编程
加强安全性是指,封装可使某个属性只能被当前类使用,从而避免被其余类或对象进行误操做。安全
如何加强安全性:使用访问修饰符private修饰属性网络
构造方法不一样于普通方法,普通方法表明对象的行为,而构造方法是提供给系统用于建立对象的方法。函数
构造方法(也称为构造函数)是一种特殊的方法,它具备如下特色。工具
构造函数的语法形式:[访问修饰符] 类名([参数列表]) ;this
类中没有构造方法时JVM会默认产生一个无参构造方法。可是若是类中已经有任意的构造方法,JVM不会再建立。spa
当使用反射建立对象时,必需要使用无参构造方法。操作系统
this指向当前对象的引用.net
this常常用于:
private int age ; ... public void setAge(int age) { ... this.age = age ; }
public Student() { //调用有一个String参数的构造方法 this(" WangYun" ); } public Student(String name) { //调用有两个参数的构造方法 this(name,23); } public Student(String name, int age) { ... }
//如下代码 Student s1 = new Student("zs",18); Student s2 = new Student("zs",18); System.out.print(s1 == s2); System.out.print(s1.getName() == s2.getName()); //执行后,控制台输出为? false true
由于setName()都是双引号字符串对象,是对常量池中同一个变量的引用。
TestStudent
,学生刘静涛也定义了一个叫 TestStudent
的类。若是在同一个文件夹下,就会产生命名冲突的问题。而使用了包的机制,就能够把王云定义的类存放在 wangyun
包下,把刘静涛定义的类存放在 liujingtao
包下,以后就能够先经过 wangyun
和 liujingtao
这样的包名,区分不一样的目录,而后再使用 TestStudent
访问两个包中各自的类,从而解决了命名冲突的问题。包的声明语法格式:package pkg1[.pkg2[.pkg3…]];
命名规则:一般包名所有用小写字母,如今使用最多的规则是使用翻转的 internet 域名(不含 www、ftp 等访问协议)。
java经常使用包:
java.lang
:lang 是 language 的简写,这个包提供 Java 语言的基础类,例如 String、Math、Integer、System 和 Thread 等。java.util
:util 是 utility 的简写,组织了 Java 的工具类,包含集合、事件模型、日期和时间设置、国际化和各类实用工具类。java.io
:io 是 input 和 output 的合并简写,指输入和输出,组织了数据流、序列化和文件系统相关的类。java.net
:net 即网络,这个包组织了为实现网络应用程序而提供的类。java.awt
:抽象窗口工具集(Abstract Window Toolkit),包含用于建立用户界面和绘制图形图像的类。引用包的两种方法:import 包名.类名;和import 包名.* ; 第二种形式的包只能引用当前包下的类,不能导入其子包中的类;同时只会导入用到的类。
import语句要写在package以后,在类定义以前。
java.lang中的类不须要import
Java 语言中的访问权限修饰符有 4 种,但却只有 3 个关键字。由于不写访问权限修饰符时,在 Java 中被称为默认权限(包权限),本课程中以 default
代替。其余 3 个访问权限修饰符分别为 private
、protected
和 public
。
修饰类:public和default。public表明类能在各类地方使用,default表明类只能在本包内使用。
修饰属性、构造方法、普通方法:private default protected public。private修饰的成员只能在本类中使用。default修饰的成员只能在本类和本包内使用。protected修饰的成员只能在本类、本包内和子类中使用。public能够在全部类中使用。
能够看到,只有成员变量/方法和类/接口才可使用访问权限修饰符,局部变量不可使用。
对象成员变量和类成员变量,对象成员变量能够在每个对象中建立,不一样对象的对象成员变量不能共享;类成员变量只建立一次,能够被全部对象共享。经过在变量前面加上static使成员变量成为静态的(类成员变量)。
能够直接经过类名引用静态变量,也能够经过实例名来引用静态变量,但推荐采用前者,由于采用后者容易混淆静态变量和实例变量。
被static修饰的方法称为静态方法/类方法,能够经过类名.方法名直接调用。
静态方法中没法使用实例变量。
Java 类首次装入 JVM 时,会对静态成员或静态块进行一次初始化,注意此时尚未产生对象。
所以,静态成员和静态块都是和类绑定的,会在类加载时就进行初始化操做。
在使用 new 关键字建立并初始化对象的过程当中,具体的初始化分为如下 4 步。
单例模式指的是不管建立了多少个引用,在堆中仅仅只有一个实例对象。
单例模式经过private修饰构造方法实现。也称为构造方法私有化。
public class Singleton { private static Singleton instance; private Singleton() { } }
同时建立一个堆内存对象供使用。这个方法在没有对象时建立,存在对象时直接返回这个对象的引用。
public static Singleton getInstance() { if (instance == null) { instance = new Singleton(); } return instance; }
继承可使得子类沿用父类的成员(属性和方法)。当多个子类具备相同的成员时,就能够考虑将这些成员提取出来,放到父类中,而后再用子类去继承这个父类,也就是将一些相同的成员提取到了更高的层次中。
继承能够大大减小冗余代码,提升代码的复用性。
继承的关键字是extends。继承的语法形式是class A extends B{}。类A称为父类、超类、基类,类B称为子类、衍生类和导出类。
子类没法继承父类的构造方法。
构造方法是一种特殊的方法,子类没法继承父类的构造方法。
子类不能继承父类中不符合访问权限的成员。
子类不能继承private修饰或者不在同包内的default修饰的父类变量。
final 关键字用法以下:
若用于修饰属性,则该属性不容许被修改。
若用于修饰父类中的方法,则该方法不容许被子类重写。
若用于修饰类,则该类不容许被其余类继承。
final修饰的方法能够被重载,但不能被重写。
super关键字用于引用父类的对象。
super()用于调用父类的构造方法来建立子类对象,和this()同样,必须写在构造方法中的第一行。
须要注意的是,子类的构造方法中若是不写 super()
,编译器会帮助你在子类构造方法的第一行加上super()
,由于在子类中调用父类构造器是“必须的”。但若是父类中只存在有参构造方法,并无提供无参构造方法,则须要在子类构造方法中显式地调用父类存在的构造器,不然可能由于父类中没有无参构造器而获得一个编译错误。
使用super调用重写方法在父类的方法:super.重写方法()。其做用是对父类方法进行一些补充。
多态能够优雅的解决程序中的扩展性问题。
在形式上,父类引用能够指向子类对象。在 Vehicle vehicle = new Car();
中,子类的 Car
对象赋值给了父类 Vehicle
引用,这称为向上转型;
在引用 vehicle
上调用方法,在运行时刻究竟调用的是父类 Vehicle
中的方法仍是子类 Car
中的方法呢?实际须要经过运行时的对象类型来判断,这称为动态绑定。
向上转型和动态绑定就是多态的具体实现机制。
向上转型的过程当中涉及了继承、重写和父类引用子类对象的概念。
向上转型的对象调用重写方法,执行的是子类的重写方法。
向上转型的好处,是不须要针对父类的多个子类再设计不一样的方法,减小了代码量而且增长了可拓展性。
动态绑定是指在编译期间方法并不会和“引用”的类型绑定在一块儿,而是在程序运行的过程当中,JVM 须要根据具体的实例对象才能肯定此时要调用的是哪一个方法。重写方法遵循的就是动态绑定。
例如,子类和父类中都有info()方法。动态绑定就是指,编译过程当中info()不会和具体的类绑定到一块儿,而是在运行期间列举出子类和父类的info()方法,根据当前的实例对象,调用该实例对象的info()方法。
静态绑定是指程序编译期的绑定。如下的类信息,使用的就是静态绑定机制:
final
、static
或 private
修饰的方法,以及重载方法和构造方法。一种推荐的编程思想是“面向基类”编程。也就是建议将面向的“对象”抽象为更高层次的基类。
向上转型的缺点:转型后的对象没法调用导出类中的方法。
向下转型:强制从基类转向衍生类。
确保向下转型的正确性:使用instance of运算符。
不管子类构造函数中有没有super();实际运行时都会调用父类的构造器。
当子类重写了父类的方法时,调用主体(即对象)和方法是运行时绑定的;
当子类和父类的属性重名时,调用主体(即对象)和属性是编译时绑定的。
这两种说法,前一种是方法的动态绑定,后一种是成员变量的静态绑定。
java在编译时肯定对象类型,同时对这个对象的成员变量进行静态编译。也就是说,sup对象的i属性是在Super类中加载的。
此时sub对象的i属性是在sub类中加载的。
首先,定义了A类型的变量ab,在编译过程当中会加载A类,此时A中的静态块会被执行,打印“1”。而后ab对象被赋值为实例化对象的引用,此时须要执行B类的构造函数,首先加载B类到内存中,此时B类中的静态块会被执行,打印"a"。而后,执行B类的构造函数。由于B类是A类的子类,因此必定会首先执行A的构造函数,所以打印“2”,而后再执行B中的构造函数,打印“b”。第二句语句,由于A类和B类都不是首次加载,因此只会调用B类的构造函数,打印“2”再打印"b"。所以打印出来结果是"1a2b2b"。
//如下代码 class Student{ int age = 1; public Student(int age) { this.age = age; } } public class Test extends Student{ public static void main(String[] args) { Test t = new Test(); System.out.println(t.age); } } //执行后,控制台输出为? 编译时错误(可见错误)
使用Test类的构造函数,会首先调用父类的构造函数,因为当前仅定义了父类的有参构造方法,因此JVM不会再建立父类的无参构造函数,所以程序出现编译时错误。
//如下代码 class Student{ public static void eat() { System.out.print(1); } } public class Test extends Student{ public static void eat() { System.out.print(2); } public static void main(String[] args) { eat(); } } //执行后,控制台输出为?2
此时,没有指明调用的是哪一个类中的重写方法,由于入口函数是子类中,因此“就近”调用子类的重写方法。
//如下代码 class Student{ public void eat() { System.out.print(1); } } public class Test extends Student{ private void eat() { System.out.print(2); } public static void main(String[] args) { Test t = new Test(); t.eat(); } } //执行后,控制台输出为?编译错误
重写方法中,权限修饰符不能比父类方法的范围小
若是范围相等,或扩大则不会报错。
//如下代码 class Student{ int i; public Student(int i) { this.i = i; } } public class Test extends Student{ int i = 1; public Test(int i) { super(10); this.i = i; } public static void main(String[] args) { Student t = new Test(100); System.out.print(t.i); } } //执行后,控制台输出为?10
编译时,首先根据静态绑定将父类t.i初始化为0;而后调用Test(100)构造函数时,首先显式调用父类有参构造函数,此时父类t.i=10,继续执行剩下的Test类构造函数,子类i=100。最后打印的是父类的t.i=10。
因为静态绑定,尽管它是子类对象,可是它在编译时绑定了父类成员变量,也会一直用父类成员变量。
//如下代码 class Student{ int i; public Student(int i) { this.i = i; } } public class Test extends Student{ int i = 1; public Test(int i) { super(10); this.i = i; } public static void main(String[] args) { Test t = new Test(100); System.out.print(t.i); } } //执行后,控制台输出为?100
同理,这个对象的类型是Test类型,因此因为静态绑定,t对象的成员变量是Test类中的成员变量。
首先子类初始化t.i=1,而后调用Test类的有参构造函数,其中先显式调用父类的有参构造函数 ,父类i成员变量初始化为0,又赋值为10,而后继续执行子类构造函数,t.i=100。最后显示的结果是子类t.i为100。
因此这种题型只须要肯定对象静态绑定的类型是什么类型(即定义为何类型),跟踪这个变量的变化就能够了。
//如下代码 class Student{ public Student() { System.out.print(1); } } public class Test extends Student{ String name; public Test(String name) { this.name = name; System.out.print(2); } public static void main(String[] args) { Test t = new Test("zs"); } } //执行后,控制台输出为?12
首先,t对象静态绑定Test类,因此t.name默认值为null。实例化调用Test类的有参构造函数时,首先调用父类的无参构造函数,打印1,而后继续执行子类的构造函数,t.name="zs",而后打印2。
//如下代码 class Student{ int age; public Student(int age) { this.age = age; } } public class Test extends Student{ public Test(int age) { super(age); } public static void main(String[] args) { Test t = new Test(10); System.out.println(t.age); } } //执行后,控制台输出为?10
子类继承了父类的age属性。
class Student1{ int age; public Student1(int age) { this.age = age; } } public class Test extends Student1{ int age; public Test(int age) { super(age); } public static void main(String[] args) { Test t = new Test(10); System.out.println(t.age); } }
若是没有继承,那么子类构造函数中仅调用父类构造函数,修改的是父类对象的属性,并无修改子类age属性,所以打印默认值0。
缘由是局部变量一旦定义就确定会被用到,须要使用者赋初值0.0不然会引发编译错误。
super()第一行的缘由:若是super()不在第一行,那么编译器会自动在第一行补充上super();
this()第一行的缘由:若是this()不在第一行,那么编译器会自动在第一行补充上super();
那么就会出现一个构造方法内,构造出多个对象的状况。JVM不容许这样的状况出现。