一、基本概念
1、Java的工作方式
- 编写源代码
- 用编译器运行源代码
- 输出字节码(任何装置都能把他转移运行)
- Java虚拟机执行
2、Java的程序结构
》 类存在于源文件中》方法存在于类中》语句存在于方法中
(1)源文件
扩展名为.Java 类用来表示程序的一个组件,类的内容必须包含在花括号里面
1 | //类 |
(2)类
类中带有一个或者多个方法,方法必须在类中声明
1 | public class Dog { |
(3)方法
一组语句构成,可以把方法想象成一个函数或者过程
1 | public class Dog { |
3、Java的类
当Java虚拟机启动时,他会寻找你在命令中指定的类。
(1)编写带有main的类
Java中所有的东西都属于这个类,你会建立源文件.java,然后将他编译成类文件.class,真正执行的是类文件
(2)语句,循环,分支
- 声明,设定,调用方法等普通语句
1 | int x = 3; |
- 反复做某件事for和while
1 | while (x > 12) { |
- 适当条件下做某事if/else
1 | if (x == 10) { |
(3)习题
- 啤酒瓶童谣程序
1 | package day1; |
- 排排看
1 | package day1; |
- 泳池迷宫
1 | class PoolPuzzleOne { |
二、类与对象
1、对象的特性
当你在设计类时,要记得对象是靠类的模型塑造出来的
- 对象是已知事物
- 对象会执行动作
- 对象本身已知的事务称为实例变量
- 对象可以执行的动作称为方法
2、类与对象的区别
- 类是创建对象的蓝图
- 根据某类创建出的对象都会有自己的实例变量
3、main()方法
- main的两种用途
- 测试真正的类
- 启动你的java应用
4、猜数字游戏
1 | class Player { |
(1)创建对象时,它会被存放在称为堆的内存区域中,不管对象如何创建都会放在此区域中。
(2)在Java的面向对象概念中并没有全局变量这回事,然而实际上会有需要方法或者常量(constant)可被任何的程序存取。任何变量只要加上public,static和final,基本上都会变成全局变量取用的常数。
5、要点
- 面向对象设计扩展功能不需改动之前已经测试好的程序代码。
- 所有的Java程序都定义在类中。类如同蓝图描述该类型的对象要如何创建。
- 对象自治﹔你无需在意它如何完成任务。
- 对象有已知的事物,并能执行工作。对象本身已知道的事物称为实例变量,它代表对象的状态。
- 对象可执行的动作称为方法,它代表对象的行为。
- 创建类时,可能同时会需要创建独立、测试用的类。
- 类可以继承自较为抽象的父类。J av a的程序在执行期是一组会互相交谈的对象。
三、primitive主数据类型和引入
1、认识变量
- 变量有两种:primitive主数据类型和引入
变量必须要有类型,另外必须要有名称
存放数值的primitive主数据类型有 向6种:
byte(8) short(16) int(32)long(64)
float(32) double(64)
1
float f = 32.5f
小数后面加上f,否则Java对带小数点的值都会被当作double处理
2、避开关键字
- 名称必须以字母、下划线(_)或$符号开头,不能用数字开头。
- 除了第一个字符之外,后面就可以用数字。反正不要用在第一个字符就行。
- 要避开Java的保留字。
3、对象引用
- 事实上没有对象变量这样的东西存在。只有引用(reference)到对象的变量。对象引用变量保存的是存取对象的方法。
- 它并不是对象的容器,而是类似指向对象的指针。或者可以说是地址。但在Java中我们不会也不该知道引用变量中实际装载的是什么,它只是用来代表单一的对象。只有Java虚拟机才会知道如何使用引用来取得该对象。
- 对primitive主数据类型中的变量来说,变量值就是所代表的值(如5、-26.7或‘a’)。对引用变量来说,变量值是取得特定对象的位表示法。
4、数组也是对象
- 数组也是对象,不管里面放着是不是primitive主数据类型
- 记得引用变量只会保留引用,而不是对象本身
5、创建一个对象
1 | package day3; |
6、要点
- 变量有两种:proimitive主数据类型和引用
- 变量的声明必须有类型和名称
- primitive主数据类型变量值是该值的字节所表示的。
- 引用变量的值代表位于堆之对象的存取方法。
- 变量有两种:primitive主数据类型和引用变量的声明必须有类型和名称。
primitive主数据类型变量值是该值的字节所表示的。 - 引用变量的值代表位于堆之对象的存取方法。
- 引用变量如同遥控器,对引用变量使用圆点运算符可以如同按下遥控器按钮般地存取它的方法或实例变量。
- 没有引用到任何对象的引用变量的值为null值。
- 数组一定是个对象,不管所声明的元素是否为primitive主数据类型,并且没有primitive主数据类型的数组,只有装载primitive主数据类型的数组。
四、方法操作实例变量
1、对象的行为
状态影响行为,行为影响状态。对象有状态和行为两种属性,分别由实例变量与方法来表示。
类所描述的是对象知道什么与执行什么
1 | class Dog { |
2、传值给方法
- 方法会运用形参。调用的方法传入实参
- 实参是传给方法的值。当他传入方法后就成了形参。参数跟局部变量是一样的。它有类型与名称,可以在方法内运用。
- 可以从方法中取返回值
- 方法可以有返回值,每个方法都声明返回的类型,把方法设定为void类型,代表并没有返回任何东西。
可以向方法中传入一个以上的值
- 调用需要两个参数的方法,并传入两个参数
- 将变量当作参数传入,需要设置好类型
Java是通过值传递的,也就是说通过拷贝传递
- 引用对象的变量所携带的是远程控制而不是对象本身。若你对方法传入参数,实际上传入的是远程控制的拷贝。
3、要点
- 类定义对象所知及所为
- 对象所知者是实例变量
- 对象所为者是方法
- 方法可依据实例变量来展示不同的行为
- 方法可使用参数,这代表你可以传入一个或者多个值给方法
- 传值给方法的参数必须符合声明时的数量,顺序和类型
- 传入与传出方法的值类型可以隐含的放大或者是明确的缩小
- 传给方法的参数值可以是直接指定的文字或者数字或者是与所声明参数相同类型的变量。
- 方法必须声明返回类型。使用void类型代表方法不返回任何东西
- 如果方法声明了非void返回的类型,那一定要返回与声明类型相同的值。
4、封装
将数据从不确定的数据改成受保护的数据,并且能修改数据的方式
封装dog
1 | class GoodDog { |
5、数组中对象的行为
声明一个装载7个Dog引用的Dog数组
1
2Dog[] pets;
pets = new Dog[7];创建两个Dog对象并赋值为数组的前两项元素
1
2pets[0] = new Dog();
pets[1] = new Dog();调用两个dog对象的方法
1
2
3pets[0].setSize(30);
int x = pets[0].getSize();
pets[1].setSize(8);
6、声明与初始化实例变量
- 实例变量永远都会有默认值,integers:0 float:0.0 booleans:false reference:null
- 局部变量没有默认值如果在变量被初始前就要使用的话,编译器会报错
五、编写程序
1、开发类
- 找出类应该做的事情
- 列出实例变量和方法
- 编写方法的伪码
- 伪码:能够帮助你专注于逻辑而不需要顾虑到程序语法
- 编写方法的测试用程序
- 需要在没有可以测试前就先写出测试用的部分,概念来自于“极限编程(XP)方法论”
- 实现类
- 测试方法
- 除错或者重新设计
2、测试代码
1 | public class SimpleDotComTestDrive { |
1 | //游戏代码 |
六、认识Java的API
1、ArrayList的操作
创建
1
ArrayList<Egg> myList = new ArrayList<Egg>()//<Egg>代表Egg类型的List
加入元素
1
2Egg s = new Egg();
myList.add(s);//此ArrayList会产生一个盒子来放Egg再加入元素
1
2Egg b = new Egg();//此ArrayList会再弄出一个盒子来放新的Egg对象
myList.add(b);查询大小
1
int theSize = myList.size();//应为myList有两个元素,size()会返回2
查询特定元素
1
boolean isIn = myList.contains(s);//因为myList带有s所引用的Egg对象,所以此方法会返回true
查询特定元素的位置
1
int idx = myList.indexOf(b);//ArrayList为0基的,所以idx返回的是2
判断集合是否为空
1
boolean empty = myList.isEmpty();//因为不是空的,isEmpty()会返回false
删除元素
1
myList.remove(s);//删除后myList 被缩小了。
2、比较arrayList与一般数组的区别
一般数组在创建的时间必须确定大小,但对于ArrayList来说,你只需要创建出此类型的对象就行。它不需要指定大小。
在java5.0中,ArrayList是参数化的,ArrayList
其中 是类型参数这代表String的集合,比如ArrayList 代表了Gog的集合
3、游戏完全码
1 | package day5; |
1 | package day5; |
1 | package day5; |
4、使用函数库(Java Api)
- 你必须指明程序代码中所使用到的类的完整名称
- 包之所以很重要有3个原因,他们可以帮助组织项目或者函数库相对于一大堆零散的类,以功能来组织会比较好。
- 包可以制造出空间名称,以便错开相同名称的类,我们可以通过不同的包来区分相同名称的类。
- 同时还能限制同一包之间的类才能相互存取,以维护安全性。
5、要点
- ArrayList是一个Java API的类
- 使用add()来新增ArrayList的元素
- 使用remove()来删除ArrayList中的元素
- 要寻找某项元素的位置,使用indexOf()
- 使用isEmpty()来判断ArrayList是否为空
- 要取得ArrayList的大小,可以使用size()方法,传统的数组使用length方法来取得大小
- 可是使用参数类型来声明数组内容的类型,例如ArrayList
- 虽然ArrayList只能携带对象而不是primitive主数据类型,但编译器能够自动将primitive主数据类型包装成Object以存放在ArrayList中
- 类会用包来组织
- 类有完整的名称,那是由包的名称和类的名称所组成的,ArrayList实际上叫做java.util.ArrayList
- 除了java.lang之外,使用到其他包的类都需要指定全名,也可以在原始程序开始的地方用import指令来说用使用到的包。
七、继承与多态
1、了解继承
在设计继承时,你会把共同的程序代码放在某个类中,然后告诉其他类这就是他们的父类,当某个类继承了另一个类时,也就是子类继承了父类。
2、设计动物仿真程序的继承树
- 找出具有共同属性和行为的对象
- 设计代表共同状态与行为的类
- 决定子类是否需要让某项行为有特定不同的运作方式
- 通过寻找使用共同行为的子类来找出更多的抽象化的机会
- 完成类的继承层次
- 当你调用对象引用的方法的时候,你会遇到与该方法类型最接近的方法。及最低阶的会胜出。
- 某物是否应该要继承另一物时,则可以用IS-A来检验,例如浴室和澡盆的关系是HAS-A而不是IS-A则它们不是继承关系。IS-A测试适用于在继承层次的任何地方
3、要点
- 子类是extends父类出来的
- 子类会继承父类所有public类型的实例变量和方法,但不会继承父类所有private类型的变量和方法
- 继承下来的方法可以被覆盖掉,但是实例变量不能被覆盖掉
- 使用IS-A测试来验证继承结构的合理性
- IS-A关系是单方面的,河马是动物,但是动物不一定是河马
- 当某个方法在子类中被覆盖过,调用这个方法时会调用到覆盖过的版本
- 如果类Y是extentds类X,且类Y是类Z的父类,则Z能通过IS-A-X的测试
4、继承
- 通过设计继承的过程,能够积累面向对象的经验值,Java程序只是由一堆类组成,因此子类不需要编译就能够运用到新版本的父类。
- 继承可以让你确保某个父类下所有类都会有父型所持有的全部方法
5、多态
在多态下,引用对象可以是不同类型。
1
Animal myDog = new Dog();
运用多态时,引用类型可以是实际对象类型的父类。
参数和返回类型也可以是多态
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16class Vet{
public void giveShot(Animal a){
//a参数可以用任何的animal的类型对象来当传入。执行到makeNoise()的时候,不管他引用的对象是什么,该对象都会执行makeNoise()
a.makeNoise();
}
}
class PetOwner{
public void start(){
Vet v = new Vet();
Dog d = new Dog();
Hippo h = new Hippo();
//giveShot这个方法可以取用任何一种Animal只要所传入的是Animal的子类它都能执行
v.giveShot(d);
v.giveShot(h);
}
}
6、内部类(防止某个类做出子类)
- 存取控制——类不能标记成私有,但可以不标记公有,非公有的类只能被同一个包的类做出子类
- 使用final这个修饰符。这表示它是继承树的末端,不能被继承。
- 如果想要防止特定的方法被覆盖,可以将该方法标识上final这个修饰符。将整个类标识成final这个修饰符,表示没有任何方法可以被覆盖
- 让类只拥有private的构造程序
7、同名异式
- 当你要覆盖父类的方法时,你就得同意要履行。比如,这个合约表示“我没有参数且返回布尔值。因此你覆盖的方法必须没有参数且返回布尔值
- 参数必须一样,且返回类型必须要兼容
- 不能降低方法的存取权限
8、方法重载
- 重载的意义是两个方法的名称相同,但参数不同所以,重载与多态毫无关系。
- 重载可以有同一个方法的多个不同参数版本以便方便调用。
- 规范:
- 返回的类型可以不同
- 不能只改变返回的类型
- 可以更改存取权限
八、接口与抽象类
1、深入多态
我们需要超越简单的继承并前进到只有通过设计与编写接口规格才能达成的适应性和扩展性,接口是一种100%的纯抽象类,它是无法初始化的。
当你设计好继承结构时,你必须要决定哪些类是抽象的,哪些类是具体的。具体的类是实际可以被初始化为对象的。设计抽象的类很简单,在抽象类之前加上关键词,abstract就好了
1
2
3abstract class Canine extends Animal{
}编辑器不不会让你初始化抽象类
1
c = new Canine();//编译器会报错
抽象类除了被继承过之外,是没有用途,没有值,没有目的的。
抽象与具体
- 不是抽象类被成为具体类
- 抽象的方法没有方法体
- 如果你声明一个抽象的方法,就必须将类也标记为抽象的,你不能在非抽象类中拥有抽象的方法。
将可继承的方法体,放在父类中是个好主意,但是有时候是没有办法坐出给任何子类都有意义的共同代码,抽象方法的意义就是算无法实现出方法的内容,但还是可以定义出一组子型共同的协议,这就是多态。
- 你想达成的目标是要使用父类型作为方法的参数,返回类型或者数组的类型。通过这个机制,你可以 加入新的子型到程序中,却又不必重写或者修改处理这些类型的程序。
2、多态的使用
编写一个类,让他可以处理所有的子类,类如编写一个animal类,让他处理Animal所有的子类,
1
2
3
4
5
6
7
8
9
10
11
12public class MyAnimal{
private Animal[] Animals = new Animal[5];//这不是创建Animal对象,只是保存Animal的数组对象
private int nextIndx = 0;
public void add(Animal a){
if(nextIndex<animals.length){
animals[nextIndex] = a;
System.out.printIn("Animal added at "+nextIndex);
nextIndex++;
}
}
}1
2
3
4
5
6
7
8
9
10
11public class AnimalTestDrive{
public static void main(String[] args){
MyAnimalList list = new MyAnimalList();
Dog a = new Dog();
Cat b = new Cat();
list.add(a);
list.add(c);
}
}
//Animal added 0
//Animal added 1在java中的所有类都是从Object这了类中继承出来的,Object是所有类的源头。
没有直接继承过其他类的类会是隐含的继承对象
从ArrayList
将Dog对象转换为原来的类型
1
2
3Object o = a1.get(index);
Dog d = (Dog) o;//将类型转换为dog
d.rom();你只能在引用变量类确实有该方法的时候才能调用它,把类的公有方法当作是合约的内容,合约是你对其他程序的承诺协议。
3、接口
所有接口都是抽象的
接口的定义
1
2
3public interface Pet{
}接口的实现
1
2
3public class Dog extends Canine implements Pet{
}设计实现pet接口
1
2
3
4
5
6
7
8
9
10
11
12
13
14public interface Pet{//接口的方法带有public 和 abstract的意义,一般省略
public abstract void beFriend();//接口方法是抽象的,没有内容,要以分号结束
public abstract void play();
}
public class Dog extend Canine implements Pet{//implents 关键词后面跟着接口的名称
//必须出现这个方法,是合约规定
public void beFriendly(){}
public void play(){}
//一般的覆盖方法
public void roam(){}
public void eat(){}
}接口的特点
- 多态:接口有无比的适用性,若用接口来替换具体的子类或者抽象的父类,作为参数或返回类型,则你就可以传入任何可以实现接口的东西,使用接口可以继承超过一个以上的来源,这样可以不同的需求组合不同的继承层次
- 接口的意义在于,你只要实现了这个接口,别人就会知道你一定会履行这个合约
- 大部分良好的设计也不需要在抽象的层次定义出实现的细节,在具体的子类上实现也是合理的。
如何判断应该是设计类,子类,抽象类或接口呢
- 如果新的类无法对其他的类通过IS-A测试时,就设计不继承其他类的类
- 只有在需要某类的特殊转化时,以覆盖或者增加的方法来继承现有的类。
- 当你需要定义一群子类的模板,又不想让程序员初始化此模板时,设计出抽线类给他们用。
- 如果想要定义出类可以扮演的角色,使用接口
调用父类的方法
创造子类,继承父类的方法后需要加入额外的方法则用关键字auper
1
2
3
4
5
6
7
8
9
10
11
12
13abstract class Report{
void runReport(){}//设置报告
void printReport(){}
}
class BuzzwordsReport extends Report{
void runReport(){
super.runReport();
buzzwordCompliance();
printReport();
}
void buzzwordCompliance(){}
}super关键字是用来引用父类的对象
4、要点
如果不想让某个类被初始化,就以abstract这个关键词将它标记为抽象的
抽象的类可以带有抽象和非抽象的方法。
如果类带有抽象的方法,则此类必定标识为抽象的
抽象的方法没有内容,它的声明是以分号结束的。
抽象的方法必须在具体的类中运行
Java所有的类都是Object(java.lang.Object)直接或者间接的子类
方法可以声明Object的参数或返回类型
不管实际上所引用的对象是什么类型,只有在引用变量的类型就是带有某方法的类型时才能调用该方法
Object引用变量在没有类型转换的情况下不能赋值给其他的类型,若堆上的对象类型与所要转换的类型不兼容,则此转换会在执行期产生异常
从ArrayList
Java不允许多重继承,因为那样会有致命方块的问题
接口就好像是100%纯天然抽象类
以interface这个关键词取代class来声明接口
实现接口时要使用implements这个关键词
class可以实现多个接口
实现某方法的类必须实现它所有的方法,因为这些方法都是public与abstract
要从子类调用父类的方法可以用super这个关键词来引用
1
super.RunReport();
九、构造器与垃圾收集器
1、对象的前世今生
你必须为对象的生命循环周期负责。你决定着对象何时创建,如何创建,也决定着何时销毁。
2、堆与栈的生存空间
在Java中程序员会在乎内存中两种
实例变量
实例变量是被声明在类而不是方法里面,他们代表每个独立的“字段”,(每个实例都能有不同的值)。实例变量存在于所属的对象中
局部变量
局部变量和方法的参数都是被声明在方法中,他们是暂时的,且生命周期只限于方法被放在堆上的这段时间。
3、方法会被堆在一起
调用方法的时候,如果方法体力还有方法,则被包含的方法会被放在上面
4、有关对象的局部变量
对象存在于何处————堆,不论对象是否声明或者创建,如果局部变量是个对该对象的引用,只有变量本身会放在栈上。
对象本身只会存在于栈上
5、要点:
- 我们关心堆与栈这两种内存空间
- 实例变量是声明在类中方法以外的地方。
- 局部变量声明在方法或者方法的参数上
- 所有局部变量都存在于栈上相对应的堆栈块中
- 对象引用变量与primitive主数据类型变量都是放在栈上
- 不管是实例变量还是局部变量,对象本身都会在堆上
1 | //如果有声明变量但没有给他赋值,则只会留下变量的空间 |
6、创建对象
- 声明对象和赋值有3个步骤,声明引用变量,创建对象,连接对象和引用
- 唯一能够调用构造函数的办法就是新建一个类
7、构建Duck
- 构造函数的一项关键特征是他会在对象能够被赋值给引用之前就被执行,这表示你可以有机会在对象被使用之前介入。
- 一般使用构造函数来初始化对象的状态,也就是说设置和给对象的实例变量赋值。
- 构造函数不会有返回类型
- 如果某对象不应该在状态被初始化之前就使用,把初始化的程序代码放在构造函数中,然后把构造函数设定成需要的参数。
- 如果你已经写了一个有参数的构造函数,还需要一个没有参数的构造函数,则你必须手动写
- 重载构造函数的意思代表你有一个以上的构造函数,且参数都不相同。
8、要点
- 实例变量保存在所属的对象中,位于堆上。
- 如果实例变量是个对对象的引用,则引用和对象都在堆上
- 构造函数是个会在新建对象的时候执行程序代码
- 构造函数必须与类同名且没有返回类型
- 你可以用构造函数来初始被创造对象的状态
- 如果你没有写构造函数,编译器会帮你安排一个
- 默认的构造函数是没有参数的。
- 如果你写了构造函数,则编译器就不会调用
- 最好能有无参数的构造函数,让人可以选择使用默认值
- 重载的构造函数意思是有超过一个以上的构造函数
- 重构的构造函数参数必须有不同参数
- 两个构造函数的参数必须不同
- 实例变量有默认值,原始的默认值是0/0/0/false 引用的默认值是,null
9、注
有时候有默认值的无参数构造函数是不合理的。例如Color这个类,该实例会代表特定的颜色,如果要用到color,就必须以某种方式指定颜色
1 | Color c = new Color(3,45,200); |
10、调用父类的构造函数
1 | public class Animal{ |
- 调用父类构造函数的时候,我们唯一的方法只能是,super();
- 如果我们没有调用super(),编译器会帮我们加上super()的调用
编译器帮忙加的一定会是没有参数的版本,假使父类有多重版本,也只有无参数的这个版本会被调用到。
从某个构造函数调用重载版的另一个构造函数
- 使用this()来从某个构造函数调用同一个类的另外一个构造函数。
- this()只能用在构造函数中,且必须是第一行语句
- super()与this()不能兼得
11、对象的生命周期
- 局部变量只会生存在声明变量的方法中
- 实例变量的寿命与对象相同,如果对象还活着,则实例变量也会是活的
- 局部变量只能在声明它的方法在执行中才能被使用
- 只要有活着的引用,对象也就会活着,如果某个对象的引用已经不在它的范围中,但此引用还是活着的,则此对象就会继续活在堆上。如果对象的唯一引用死了,对象就会被从堆中被踢开,引用变量会跟堆栈块一起解散。
- 释放引用变量的三种方法
- 引用永久性的离开它的范围
- 引用被赋值到其他的对象上
- 直接将引用设定为null
12、静态方法不需要实例化
例如math,我们用到的是类本身
1
2
3int x = Math.round(42.2);
int y = Math.min(56,12);
int z = Math.abs(-342);
13、静态变量和非静态变量的区别
java 是面向对象的,但是若处于某种特殊的情况下,通常是实用方法,则不需要类的实例,static这个关键词可以标记出不需要实例的方法。一个静态的方法代表说“一种不依靠实例变量也就不需要对象的行为”。
静态的调用方法,以类的名称调用静态的方法
1
Math.min(11,88);
以引用变量的名称调用非静态的方法
1
2Song t2 = new Song();
t2.play();