6 Java基础整理 第六-八章
1.封装
封装的目的是简化编程和增强安全性。
简化编程是指,封装可以让使用者不必了解具体类的内部实现细节,而只是要通过提供给外部访问的方法来访问类中的属性和方法
增强安全性是指,封装可以使某个属性只能被当前类使用,从而避免被其他类或对象进行误操作。
如何增强安全性:使用访问修饰符private修饰属性
2.构造函数
构造方法不同于普通方法,普通方法代表对象的行为,而构造方法是提供给系统用于创建对象的方法。
构造方法(也称为构造函数)是一种特殊的方法,它具有以下特点。
- 构造方法的方法名必须与类名相同。
- 构造方法没有返回类型,在方法名前不声明返回类型(包括 void)。
- 但其实构造方法是有返回值的,返回的是刚刚被初始化完成的当前对象的引用
构造函数的语法形式:[访问修饰符] 类名([参数列表]) ;
类中没有构造方法时JVM会默认产生一个无参构造方法。但是如果类中已经有任意的构造方法,JVM不会再创建。
当使用反射创建对象时,必须要使用无参构造方法。
3.this关键字:
this指向当前对象的引用
this经常用于:
- 区分成员变量和局部变量,避免命名的烦恼。
- 调用其他构造方法。 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) {
...
}
4.练习
//以下代码
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()都是双引号字符串对象,是对常量池中同一个变量的引用。
5.Java包
- 为了更好地组织类,Java 提供了包机制。包是类的容器,用于分隔类名空间。
- 如果没有指定包名,所有的类都属于一个默认的无名包。
- Java 中将实现相关功能的类组织到一个包中。
- 提供了类似于操作系统树形文件夹的组织形式,能分门别类地存储、管理类,易于查找并使用类。
- 解决了同名类的命名冲突问题。例如,学生王云定义了一个类,类名叫
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
6.Java的权限修饰符:
Java 语言中的访问权限修饰符有 4 种,但却只有 3 个关键字。因为不写访问权限修饰符时,在 Java 中被称为默认权限(包权限),本课程中以 default
代替。其他 3 个访问权限修饰符分别为 private
、protected
和 public
。
修饰类:public和default。public代表类能在各种地方使用,default代表类只能在本包内使用。
修饰属性、构造方法、普通方法:private default protected public。private修饰的成员只能在本类中使用。default修饰的成员只能在本类和本包内使用。protected修饰的成员只能在本类、本包内和子类中使用。public可以在所有类中使用。
可以看到,只有成员变量/方法和类/接口才可以使用访问权限修饰符,局部变量不可以使用。
7.成员变量:
对象成员变量和类成员变量,对象成员变量可以在每一个对象中创建,不同对象的对象成员变量不能共享;类成员变量只创建一次,可以被所有对象共享。通过在变量前面加上static使成员变量成为静态的(类成员变量)。
可以直接通过类名引用静态变量,也可以通过实例名来引用静态变量,但推荐采用前者,因为采用后者容易混淆静态变量和实例变量。
被static修饰的方法称为静态方法/类方法,可以通过类名.方法名直接调用。
静态方法中无法使用实例变量。
Java 类首次装入 JVM 时,会对静态成员或静态块进行一次初始化,注意此时还没有产生对象。
因此,静态成员和静态块都是和类绑定的,会在类加载时就进行初始化操作。
在使用 new 关键字创建并初始化对象的过程中,具体的初始化分为以下 4 步。
- 给对象的实例变量分配空间,默认初始化成员变量。
- 成员变量声明时的初始化
- 初始化块初始化。
- 构造方法初始化。
8.单例模式:
单例模式指的是无论创建了多少个引用,在堆中仅仅只有一个实例对象。
单例模式通过private修饰构造方法实现。也称为构造方法私有化。
public class Singleton {
private static Singleton instance;
private Singleton() {
}
}
同时创建一个堆内存对象供使用。这个方法在没有对象时创建,存在对象时直接返回这个对象的引用。
public static Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
9.继承
继承可以使得子类沿用父类的成员(属性和方法)。当多个子类具有相同的成员时,就可以考虑将这些成员提取出来,放到父类中,然后再用子类去继承这个父类,也就是将一些相同的成员提取到了更高的层次中。
继承可以大大减少冗余代码,提高代码的复用性。
继承的关键字是extends。继承的语法形式是class A extends B{}。类A称为父类、超类、基类,类B称为子类、衍生类和导出类。
子类无法继承父类的构造方法。
构造方法是一种特殊的方法,子类无法继承父类的构造方法。
子类不能继承父类中不符合访问权限的成员。
子类不能继承private修饰或者不在同包内的default修饰的父类变量。
10.final关键字
final 关键字用法如下:
若用于修饰属性,则该属性不允许被修改。
若用于修饰父类中的方法,则该方法不允许被子类重写。
若用于修饰类,则该类不允许被其他类继承。
final修饰的方法可以被重载,但不能被重写。
11.重写
- 重写方法与被重写方法同名,参数列表也必须相同。
- 重写方法的返回值类型必须和被重写方法的返回值类型相同或是其子类。
- 重写方法不能缩小被重写方法的访问权限。
- 子类不能重写父类的构造方法
12.super关键字
super关键字用于引用父类的对象。
super()用于调用父类的构造方法来创建子类对象,和this()一样,必须写在构造方法中的第一行。
需要注意的是,子类的构造方法中如果不写 super()
,编译器会帮助你在子类构造方法的第一行加上super()
,因为在子类中调用父类构造器是“必须的”。但如果父类中只存在有参构造方法,并没有提供无参构造方法,则需要在子类构造方法中显式地调用父类存在的构造器,否则可能因为父类中没有无参构造器而得到一个编译错误。
使用super调用重写方法在父类的方法:super.重写方法()。其作用是对父类方法进行一些补充。
13.多态
多态可以优雅的解决程序中的扩展性问题。
在形式上,父类引用可以指向子类对象。在 Vehicle vehicle = new Car();
中,子类的 Car
对象赋值给了父类 Vehicle
引用,这称为向上转型;
在引用 vehicle
上调用方法,在运行时刻究竟调用的是父类 Vehicle
中的方法还是子类 Car
中的方法呢?实际需要通过运行时的对象类型来判断,这称为动态绑定。
向上转型和动态绑定就是多态的具体实现机制。
向上转型的过程中涉及了继承、重写和父类引用子类对象的概念。
向上转型的对象调用重写方法,执行的是子类的重写方法。
向上转型的好处,是不需要针对父类的多个子类再设计不同的方法,减少了代码量并且增加了可拓展性。
动态绑定是指在编译期间方法并不会和“引用”的类型绑定在一起,而是在程序运行的过程中,JVM 需要根据具体的实例对象才能确定此时要调用的是哪个方法。重写方法遵循的就是动态绑定。
例如,子类和父类中都有info()方法。动态绑定就是指,编译过程中info()不会和具体的类绑定到一起,而是在运行期间列举出子类和父类的info()方法,根据当前的实例对象,调用该实例对象的info()方法。
静态绑定是指程序编译期的绑定。以下的类信息,使用的就是静态绑定机制:
final
、static
或private
修饰的方法,以及重载方法和构造方法。- 成员变量。Java不支持(实际上是不鼓励)成员变量的多态行为。
一种推荐的编程思想是“面向基类”编程。也就是建议将面向的“对象”抽象为更高层次的基类。
向上转型的缺点:转型后的对象无法调用导出类中的方法。
向下转型:强制从基类转向衍生类。
确保向下转型的正确性:使用instance of运算符。
14.练习
无论子类构造函数中有没有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。
15.为什么局部变量不能被赋初值?
原因是局部变量一旦定义就肯定会被用到,需要使用者赋初值0.0否则会引起编译错误。
16.为什么只能在第一行调用this()和super()构造方法?
super()第一行的原因:如果super()不在第一行,那么编译器会自动在第一行补充上super();
this()第一行的原因:如果this()不在第一行,那么编译器会自动在第一行补充上super();
那么就会出现一个构造方法内,构造出多个对象的情况。JVM不允许这样的情况出现。
6 Java基础整理 第六-八章的更多相关文章
- 2015年12月28日 Java基础系列(六)流
2015年12月28日 Java基础系列(六)流2015年12月28日 Java基础系列(六)流2015年12月28日 Java基础系列(六)流
- java基础解析系列(六)---深入注解原理及使用
java基础解析系列(六)---注解原理及使用 java基础解析系列(一)---String.StringBuffer.StringBuilder java基础解析系列(二)---Integer ja ...
- java基础解析系列(六)---注解原理及使用
java基础解析系列(六)---注解原理及使用 java基础解析系列(一)---String.StringBuffer.StringBuilder java基础解析系列(二)---Integer缓存及 ...
- 机器学习 —— 基础整理(六)线性判别函数:感知器、松弛算法、Ho-Kashyap算法
这篇总结继续复习分类问题.本文简单整理了以下内容: (一)线性判别函数与广义线性判别函数 (二)感知器 (三)松弛算法 (四)Ho-Kashyap算法 闲话:本篇是本系列[机器学习基础整理]在time ...
- JAVA基础整理-集合篇(一)
集合作为JAVA的基础知识,本来感觉自己理解的很清楚了,但是在最近的一次面试中还是答得不尽如人意!再次做一下整理,以便加深理解以及随时查阅. 首先,java.util包中三个重要的接口及特点:List ...
- Java基础整理
一.Java中的遍历 1.在java开发中会碰到遍历List删除其中多个元素的情况,如果使用一般的for循环以及增强的for循环,代码会抛出异常ConcurrentModificationExcept ...
- JAVA基础部分复习(六、常用关键字说明)
/** * JAVA中常用关键字复习 * final * finalize * finally * * @author dyq * */ public class KeyWordReview exte ...
- Java基础整理之字节、数组、字符串、面向对象
一.字节(8个)8bit = 1B或1byte1024B = 1Kb 二.强制类型转换顺序及其大小顺序遵循向上转换的规则byte,short,char -> int -> long -&g ...
- Java基础学习(六)-- 递归以及文件I/O流基础详解
递归 1.递归的概念: 在函数自身内部,调用函数本身的方式,称为递归. 2.递归的注意事项:包括递进去,归出来两步. 即:首先依次执行[函数调自身语句]上半部分的代码,知道最里层.(递进去),然后 ...
随机推荐
- redis 记一次搭建高可用redis集群过程,问题解决;Node 192.168.184.133:8001 is not configured as a cluster node
------------恢复内容开始------------ 步骤 1:每台redis服务器启动之后,需要将这几台redis关联起来, 2: 关联命令启动之后 报错: Node 192.168.184 ...
- 从简单示例看对象的创建过程, 为什么双重检查的单例模式,分析Volatile关键字不能少
编译指令 :javac Test.java 反编译指令: javap -v Test 代码 public class ObjectTest { int m = 8; public static voi ...
- Android adb的常用命令
环境部署: 1.下载adb工具 2.下载奇兔刷机(或其它一键刷机软件),将手机与电脑进行连接 3.一键root手机 命令 1.获取设备列表及设备状态:adb devices 如果连接的设备不止一个, ...
- 查找文件与cron计划任务
查找文件 • 根据预设的条件递归查找对应的文件 find [目录] [条件1] [-a|-o] [条件2] ... -type 类型(f文件.d目录.l快捷方式) -name "文档名称 ...
- 量子:基于ERP块对的两步量子直接通信
学习论文: 题目:Two-step quantum direct communication protocol using the Einstein-Podolsky-Rosen pair block ...
- Spring Reactor 入门与实践
适合阅读的人群:本文适合对 Spring.Netty 等框架,以及 Java 8 的 Lambda.Stream 等特性有基本认识,希望了解 Spring 5 的反应式编程特性的技术人员阅读. 一.前 ...
- 干货 | LuatOS BSP移植教程,简单到复制粘贴!!!
LuatOS本着自身的开源特性,可以很轻松的嵌入到很多微处理器和微控制器.今天简要讲下如何移植这套系统,上手比较简单,看完基本就会了. 要想做移植,就要先了解需要移植芯片的SDK,LuatOS依赖于F ...
- 【题解】coin HDU2884 多重背包
题目 Coins Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)Total Submi ...
- google protobuf的原理和思路提炼
之前其实已经用了5篇文章完整地分析了protobuf的原理.回过头去看,感觉一方面篇幅过大,另一方面过于追求细节和源码,对protobuf的初学者并不十分友好,因此这篇文章将会站在"了解.使 ...
- .net core Redis消息队列中间件【InitQ】
前言 这是一篇拖更很久的博客,不知不觉InitQ在nuget下载量已经过15K了,奈何胸无点墨也不晓得怎么写(懒),随便在github上挂了个md,现在好好唠唠如何在redis里使用队列 队列缓存分布 ...