
该系列博文会告诉你如何从入门到进阶一步步地学习Java基础知识并上手进行实战接着了解每个Java知识点背后的实现原理更完整地了解整个Java技术体系形成自己的知识框架。概述Java是面向对象的程序设计语言Java语言提供了定义类、成员变量、方法等最基本的功能。类可被认为是一种自定义的数据类型可以使用类来定义变量所有使用类定义的变量都是引用变量它们将会引用到类的对象。类用于描述客观世界里某一类对象的共同特征而对象则是类的具体存在Java程序使用类的构造器来创建该类的对象。对象和类Java是面向对象的程序设计语言类是面向对象的重要内容可以把类当成一种自定义类型可以使用类来定义变量这种类型的变量统称为引用变量。也就是说所有类是引用类型。对象是由类创建出来的可以说类时对象的抽象对象是类的实例。对象的概念Java 是面向对象的编程语言对象就是面向对象程序设计的核心。所谓对象就是真实世界中的实体对象与实体是一一对应的也就是说现实世界中每一个实体都是一个对象它是一种具体的概念。对象有以下特点对象具有属性和行为。对象具有变化的状态。对象具有唯一性。对象都是某个类别的实例。一切皆为对象真实世界中的所有事物都可以视为对象。面向对象与面向过程1、面向过程面向过程是一种以事件为中心的编程思想编程的时候把解决问题的步骤分析出来然后用函数把这些步骤实现在一步一步的具体步骤中再按顺序调用函数。我们以五子棋为例来解释一下面向过程是如何解决问题的下过五子棋的同学都知道首先要找两个人然后把棋谱摆放好其中一方手持黑棋另一方手持白旗一般约定白棋先动然后黑棋在动这样每人一步直到某一方先凑成五子一条线便为赢。这是我们平常下五子棋的过程那么用面向过程该如何表示呢我们可以将下五子棋的过程抽象成如下步骤1开始游戏2黑子先走3绘制画面4判断输赢5轮到白子6绘制画面7判断输赢8返回步骤2 9输出最后结果。接着我们用面向过程来实现五子棋这一程序代码语言javascriptAI代码解释下五子棋{ 开始游戏 黑子先走 绘制画面 判断输赢 轮到白子 绘制画面 判断输赢 返回到 黑子先走 输出最后结果 }可见面向过程始终关注的是怎么一步一步地判断棋局输赢的通过控制代码从而实现函数的顺序执行。2、面向对象 一种基于面向过程的新编程思想顾名思义就是该思想是站在对象的角度思考问题我们把多个功能合理放到不同对象里强调的是具备某些功能的对象。具备某种功能的实体称为对象。面向对象最小的程序单元是类。面向对象更加符合常规的思维方式稳定性好可重用性强易于开发大型软件产品有良好的可维护性。Java编程思想一书中有一段对面向对象的总结非常清晰到位可谓是面向对象的精华所在1、万物皆对象2、程序时对象的集合它们通过发送消息来告知彼此所需要做的3、每个对象都有自己的由其他对象所构成的存储4、每个对象都拥有其类型5、某一特定类型的所有对象都可以接收同样的消息3、两者优缺点比较1、面向过程优点流程化使得编程任务明确在开发之前基本考虑了实现方式和最终结果具体步骤清楚便于节点分析。效率高面向过程强调代码的短小精悍善于结合数据结构来开发高效率的程序。缺点需要深入的思考耗费精力代码重用性低扩展能力差后期维护难度比较大。2、面向对象优点:结构清晰程序是模块化和结构化更加符合人类的思维方式易扩展代码重用率高可继承可覆盖可以设计出低耦合的系统易维护系统低耦合的特点有利于减少程序的后期维护工作量。缺点开销大当要修改对象内部时对象的属性不允许外部直接存取所以要增加许多没有其他意义、只负责读或写的行为。这会为编程工作增加负担增加运行开销并且使程序显得臃肿。性能低由于面向更高的逻辑抽象层使得面向对象在实现的时候不得不做出性能上面的牺牲计算时间和空间存储大小都开销很大。面向对象的三大特性概述1、继承继承是面向对象的三大特征之一也是实现软件复用的重要手段。Java的继承具有单继承的特点每个子类只有一个直接父类。2、封装封装Encapsulation是面向对象的三大特征之一它指的是将对象的状态信息隐藏在对象内部不允许外部程序直接访问对象内部信息而是通过该类所提供的方法来实现对内部信息的操作和访问。封装是面向对象编程语言对客观世界的模拟在客观世界里对象的状态信息都被隐藏在对象内部外界无法直接操作和修改。比如说一个人的年龄年龄只会随着时间的流逝而逐渐增长不能随意修改人的年龄。对一个类或对象实现良好的封装可以实现以下目的。3、多态Java引用变量有两个类型一个是编译时类型一个是运行时类型。编译时类型由声明该变量时使用的类型决定运行时类型由实际赋给该变量的对象决定。如果编译时类型和运行时类型不一致就可能出现所谓的多态Polymorphism。多态的作用是消除类型之间的耦合关系。详解一、继承1、继承的概念程序来源于生活也为生活所用。我们先从生活中的例子来看一下什么是继承现在有一个农场主家有良田万顷每年收入很多他有一个儿子就是我们口中的富二代。有一天农场主不幸去世了那么他手下的农田和财产都是谁的了毫无疑问当然是他儿子的了如果你好好努力将来你儿子有很大机会是富二代哦。那么他儿子本来一无所有现在顷刻间多了需要Money农田房子等等也就是拥有了他父亲的所有物资财富这个我们就称之为继承。Java的继承通过extends关键字来实现实现继承的类被称为子类被继承的类被称为父类有的也称其为基类、超类。父类和子类的关系是一种一般和特殊的关系。例如水果和苹果的关系苹果继承了水果苹果是水果的子类则苹果是一种特殊的水果。代码语言javascriptAI代码解释class Fruit{ public double weight; public void info() { System.out.println(我是一个水果重weightg); } } public class Apple extends Fruit{ public static void main(String[] args) { //创建Apple对象 Apple applenew Apple(); //Apple对象本身并没有weight成员变量 //但是Apple的父类用于weight变量所以Apple也可以方位 apple.weight88; apple.info(); } } 结果我是一个水果重88.0g2、重写父类的方法子类扩展了父类子类是一个特殊的父类。大部分时候子类总是以父类为基础额外增加新的成员变量和方法。但有一种情况例外子类需要重写父类的方法。例如鸟类都包含了飞翔方法其中鸵鸟是一种特殊的鸟类因此鸵鸟应该是鸟的子类因此它也将从鸟类获得飞翔方法但这个飞翔方法明显不适合鸵鸟为此鸵鸟需要重写鸟类的方法。代码语言javascriptAI代码解释//父类 class Bird{ public void fly() { System.out.println(我在天空自由的飞翔); } } public class Ostrich extends Bird { //重写Bird的fly方法 public void fly() { System.out.println(我只能在地上奔跑); } public static void main(String[] args) { //创建Ostrich对象 Ostrich ostrichnew Ostrich(); ostrich.fly(); } } 结果我只能在地上奔跑重写时需要注意1、返回值类型2、方法名3、参数类型及个数都要与父类继承的方法相同才叫方法的重写。重写与重载的区别重写相对继承而言子类中对父类已经存在的方法进行区别化的修改。重载在同一个类中处理不同数据的多个相同方法名的多态手段。重载方法名相同参数列表不同。3、继承的初始化顺序先思考一下下面代码的输出结果代码语言javascriptAI代码解释class Animal{ public Animal() { System.out.println(我是父类动物); } } class Humanity extends Animal{ public Humanity() { System.out.println(我是父类人类); } } public class Student extends Humanity{ public Student() { System.out.println(我是子类学生); } public static void main(String[] args) { Student studentnew Student(); } }不要看结果自己先思考一下输出结果代码语言javascriptAI代码解释结果是不是和你思考的结果一样不一样的同学接着往下看Java中继承初始化顺序如下1、初始化父类再初始化子类2、先执行初始化对象中属性再执行构造方法中的初始化。基于上面两点我们就知道实例化一个子类java程序的执行顺序是父类对象属性初始化----父类对象构造方法----子类对象属性初始化—子类对象构造方法下面放上一张形象的图4、final关键字final 关键字可用于修饰类、变量和方法final关键字有点类似C#里的sealed关键字用于表示它修饰的类、方法和变量不可改变。final修饰变量时表示该变量一旦获得了初始值就不可被改变final既可以修饰成员变量包括类变量和实例变量也可以修饰局部变量、形参。有的书上介绍说final修饰的变量不能被赋值这种说法是错误的严格的说法是final修饰的变量不可被改变一旦获得了初始值该final变量的值就不能被重新赋值。由于final变量获得初始值之后不能被重新赋值因此final修饰成员变量和修饰局部变量时有一定的不同。1、final修饰变量☆final修饰成员变量一旦有了初始值就不能被重新赋值。final修饰的成员变量必须由程序显示的指定初始值。final修饰类变量必须在静态初始化块中指定初始值或声明该类变量时指定初始值而且只能在两个地方其中之一指定final修饰实例变量必须在非静态初始化块、声明该实例变量或构造器中指定初始值而且只能在三个地方的其中之一指定。☆final修饰局部变量系统不会对局部变量进行初始化局部变量必须由程序员显式初始化。因此使用final修饰局部变量时既可以在定义时指定默认值也可以不指定默认值。☆final修饰基本类型变量与引用类型变量区别当使用final修饰基本类型变量时不能对基本类型变量重新赋值因此基本类型变量不能被改变。但对于引用类型变量而言它保存的仅仅是一个引用final只保证这个引用类型变量所引用的地址不会改变即一直引用同一个对象但这个对象完全可以发生改变。代码语言javascriptAI代码解释import java.util.Arrays; public class FinalTest { public static void main(String[] args) { //final修饰数组变量arr只是一个引用变量 final int[] arr {3,90,33,12}; System.out.println(Arrays.toString(arr)); //对数组进行排序合法 Arrays.sort(arr); System.out.println(Arrays.toString(arr)); //对数组元素进行赋值合法 arr[2]109; System.out.println(Arrays.toString(arr)); //下面语句对arr重新赋值非法 //arrnull; } }2、final修饰方法final修饰的方法不可被重写3、final修饰类final修饰的类不可以有之类5、this关键字this是自身的一个对象代表对象本身可以理解为指向对象本身的一个指针。this的用法1、普通的直接引用2、形参与成员名字重名用this来区分代码语言javascriptAI代码解释public class Student{ String username; String password; public Student(String username,String password) { //this 代表当前对象 也就是下面的student this.usernameusername; this.passwordpassword; } public static void main(String[] args) { Student studentnew Student(jack,123); } }3、引用构造函数这个放在super关键字中说6、super关键字super可以理解为是指向自己超父类对象的一个指针而这个超类指的是离自己最近的一个父类。super的用法1、普通的直接引用与this类似super相当于是指向当前对象的父类super.name引用父类的变量super.add()引用父类的方法2、子类中的成员变量或方法与父类中的成员变量或方法同名代码语言javascriptAI代码解释class Humanity{ public void eat() { System.out.println(动物吃肉); } } public class Student extends Humanity{ public void eat() { //super调用父类中的同名方法 super.eat(); System.out.println(人吃饭); } public static void main(String[] args) { Student studentnew Student(); student.eat(); } }结果代码语言javascriptAI代码解释动物吃肉 人吃饭3、引用构造函数super参数调用父类中的某一个构造函数应该为构造函数中的第一条语句。this参数调用本类中另一种形式的构造函数应该为构造函数中的第一条语句。代码语言javascriptAI代码解释class Humanity{ public Humanity() { System.out.println(父类无参构造); } public Humanity(String s) { System.out.println(父类有参构造s); } } public class Student extends Humanity{ public Student() { super();//调用父类的无参构造方法 System.out.println(子类无参构造); } public Student(String s) { super(s);//调用父类的有参构造 System.out.println(子类的有参构造s); } public Student(String username,String password) { this(username);//调用本类的有参构造 System.out.println(子类带两个参数的构造函数usernamepassword); } public static void main(String[] args) { Student studentnew Student(); Student student2new Student(小明); Student student3new Student(小明,123); } }输出结果代码语言javascriptAI代码解释父类无参构造 子类无参构造 父类有参构造小明 子类的有参构造小明 父类有参构造小明 子类的有参构造小明 子类带两个参数的构造函数小明123二、封装1、封装的概念与优点封装Encapsulation是面向对象的三大特征之一它指的是将对象的状态信息隐藏在对象内部不允许外部程序直接访问对象内部信息而是通过该类所提供的方法来实现对内部信息的操作和访问。封装是面向对象编程语言对客观世界的模拟在客观世界里对象的状态信息都被隐藏在对象内部外界无法直接操作和修改。就如刚刚说的Person对象的age变量只能随着岁月的流逝age才会增加通常不能随意修改Person对象的age。对一个类或对象实现良好的封装可以实现以下目的。隐藏类的实现细节。让使用者只能通过事先预定的方法来访问数据从而可以在该方法里加入控制逻辑限制对成员变量的不合理访问。可进行数据检查从而有利于保证对象信息的完整性。便于修改提高代码的可维护性。为了实现良好的封装需要从两个方面考虑。将对象的成员变量和实现细节隐藏起来不允许外部直接访问。把方法暴露出来让方法来控制对这些成员变量进行安全的访问和操作。2、访问修饰符Java提供了3个访问修饰符public、protected和private另外还有一个默认的修饰符defaultJava的访问控制级别如下图所示下面来详细介绍一下四个访问修饰符private当前类访问权限如果类里的一个成员包括成员变量、方法和构造器等使用private访问控制符来修饰则这个成员只能在当前类的内部被访问。很显然这个访问控制符用于修饰成员变量最合适使用它来修饰成员变量就可以把成员变量隐藏在该类的内部。default包访问权限如果类里的一个成员包括成员变量、方法和构造器等或者一个外部类不使用任何访问控制符修饰就称它是包访问权限的default 访问控制的成员或外部类可以被相同包下的其他类访问。关于包的介绍请看5.4.3节。protected子类访问权限如果一个成员包括成员变量、方法和构造器等使用protected访问控制符修饰那么这个成员既可以被同一个包中的其他类访问也可以被不同包中的子类访问。在通常情况下如果使用protected来修饰一个方法通常是希望其子类来重写这个方法。public公共访问权限这是一个最宽松的访问控制级别如果一个成员包括成员变量、方法和构造器等或者一个外部类使用public访问控制符修饰那么这个成员或外部类就可以被所有类访问不管访问类和被访问类是否处于同一个包中是否具有父子继承关系。掌握了访问修饰符后我们就可以来使用封装了。代码语言javascriptAI代码解释public class Person { private String username; private Integer age; public String getUsername() { return username; } public void setUsername(String username) { this.username username; } public Integer getAge() { return age; } public void setAge(Integer age) { this.age age; } }上述代码Person类中有两个成员变量username和age它们都是私有变量外部不能访问但是提供了get和set方法通过这两个方法便可以修改和获取Person类中的相关数据这就是封装3、java中的包是什么记得我上初中的时候班级有两个同名的同学都叫王健老师每次叫王健的时候他俩不知道叫的是谁后来加上性别区分一个叫男王健一个叫女王健这次区分开。那么我们在java中会不会遇到这种情况呢当然会比如就Person这个类而言在一个大型项目中多人协作开发你写了一个类叫Person我也写个类叫Person那么该如何区分这两个类呢总不能一个叫男Person一个叫女Person吧哈哈。这时候java就引入了包的机制允许在类名前面加上一个前缀来限制这个类提供了类的多层命名空间注意1、package语句必须作为源文件的第一条非注释性语句一个源文件只能指定一个包即只能包含一条package语句该源文件中可以定义多个类则这些类将全部位于该包下。 2、如果没有显式指定package语句则处于默认包下。在实际企业开发中通常不会把类定义在默认包下但本书中的大量示例程序为了简单起见都没有显式指定package语句。 3、同一个包下的类可以自由访问三、多态1、多态的概念多态是指允许不同类的对象对同一消息做出响应。即同一消息可以根据发送对象的不同而采用多种不同的行为方式。发送消息就是函数调用2、多态的作用消除类型之间的耦合关系。3、多态产生的条件1、要有继承2、要有重写3、父类引用指向子类对象。4、多态的优点1、可替换性substitutability。多态对已存在代码具有可替换性。例如多态对圆Circle类工作对其他任何圆形几何体如圆环也同样工作。2、可扩充性extensibility。多态对代码具有可扩充性。增加新的子类不影响已存在类的多态性、继承性以及其他特性的运行和操作。实际上新加子类更容易获得多态功能。例如在实现了圆锥、半圆锥以及半球体的多态基础上很容易增添球体类的多态性。3、接口性interface-ability。多态是超类通过方法签名向子类提供了一个共同接口由子类来完善或者覆盖它而实现的。如图8.3 所示。图中超类Shape规定了两个实现多态的接口方法computeArea()以及computeVolume()。子类如Circle和Sphere为了实现多态完善或者覆盖这两个接口方法。4、灵活性flexibility。它在应用中体现了灵活多样的操作提高了使用效率。5、简化性simplicity。多态简化对应用软件的代码编写和修改过程尤其在处理大量对象的运算和操作时这个特点尤为突出和重要。Java中多态的实现方式接口实现继承父类进行方法重写同一个类中进行方法重载。5、经典的多态案例代码语言javascriptAI代码解释class Animal{ public void eat() { } } class Cat extends Animal { public void eat() { System.out.println(吃鱼); } public void catchMouse() { System.out.println(抓老鼠); } } class Dog extends Animal { public void eat() { System.out.println(吃骨头); } public void kanJia() { System.out.println(看家); } } public class AnimalTest { public static void main(String[] args) { Animal catnew Cat();//向上转型 cat.eat(); Animal dognew Dog(); dog.eat(); //Cat c(Cat)cat;//向上转型 } }Animal是父类它有两个之类分别是Dog和Cat之类分别重写了父类的eat方法。输出结果代码语言javascriptAI代码解释吃鱼 吃骨头从输出结果可以看出同样都是Animal但是却有不同的行为表现这就是多态6、向上转型和向下转型1、向上转型就以上述的父类Animal和一个子类Dog来说明当父类的引用可以指向子类的对象时就是向上类型转换Animal catnew Cat();2、向下转型向下类型转换(强制类型转换)是大类型转换到小类型(有风险,可能出现数据溢出)。例如Cat c(Cat)cat7、重写和重载重写父类与子类之间的多态性对父类的函数进行重新定义。如果在子类中定义某方法与其父类有相同的名称和参数我们说该方法被重写 (Overriding)。在Java中子类可继承父类中的方法而不需要重新编写相同的方法。重载方法重载是让类以统一的方式处理不同类型数据的一种手段。多个同名函数同时存在具有不同的参数个数/类型。重载是一个类中多态性的一种表现。重载发生在一个类当中重写发生在两个类当中但是这两个类是父子类的关系。