我的java学习之旅
第一课:Java运行原理的阐述
Java程序运行时必须经过两个步骤:编译和运行。
首先将扩展名为 java 的源文件编译,生扩展名为 class 的字节码文件,然后再经过 Java 虚拟机将字节码文件进行解释执行。
具体步骤如下:
(1) 编写一个 Java 源文件;
(2) 通过 “javac” 命令开启Java编译器进行编译;
(3) 编译结束生成 class 字节码文件;
(4) 再使用 “java” 命令启动 Java 虚拟机运行程序;
(5) 执行后生成计算机可识别的机器码文件,并在计算机上显示执行结果。
不难发现,Java程序是由Java虚拟机负责执行的,而并非操作系统。这样的好处在于,Java程序可实现跨平台运行,即在不同的操作系统上可以运行同一段且具有相同作用代码,前提是系统安装了对应的虚拟机。
像Windows,Linux,MacOS 等 都有对应的虚拟机。
Day2
第二课 Java编程基础(摘要)
第一小节——标识符
定义:是由任意顺序的大小写英文字母、数字、下划线(’’_’’)和美元符号(“$”)组成的字符串,但要注意三点:
- 不能以数字开头 (错误示例: 3num、4age )
- 不能是Java中的关键字 (错误示例:class、int)
- 中间不能有 空格 和其他符号 (错误示例:“stu name” 、“stu\score”)
另外,为了增加代码的可读性和美观性,除了要严格遵循以上规则,也应当遵循以下规则:
(1)包名字母一律小写;
(2)类名和接口名每个单词的首字母一律大写;
(3)变量名有两个及两个以上单词组成时,从第二个单词开始后面单词的首字母一律大写(eg:studentName);
(4)常量名所有字母都要大写,由多个单词组成时单词之间用下划线连接(eg:DAY_OF_MONTH);
(5)尽量用有实际意义的单词来定义标识符。
第二小节——数据类型
基本数据包括两大类:基本数据类型和引用数据类型
基本数据类型又可以分为 数值型、字符型(char)和布尔型(boolean);其中布尔型又包括四个整形(byte、short、int、long)和两个两个浮点型(float、double)
基本数据类型 | 所占字节数 |
---|---|
byte | 1 |
short | 2 |
int | 4 |
long | 8 |
float | 4 |
double | 8 |
char | 2 |
boolean | 1 or 4 |
值得注意的是,boolean 型只有 true和 false两个变量。
另外,long 和 int同占四个字节,而long型的变量范围远大于int的原因是 两者储存数据的结构不同。
第三小节——位运算与逻辑运算
位运算符有:按位与(&)、按位或(|)、按位异或(^)、取反(~)、左移(<<)、右移(>>)、无符号右移(>>>)。
逻辑运算符有:按位与(&)、按位或(|)、按位异或(^)、非(!)、短路与(&&)、短路或(||)。
两者都是 双目运算符
具体运算细则略。
区别:
- 位运算的运算对象是 二进制数,结果依然是一个二进制数,而逻辑运算的对象是“true”和“false”,结果依然是“true”或“false”。
- “&&”和“&”区别在于:
当“&&”左边的结果是“false”时,右边运算式不再进行计算,最终结果是“false”。 而“&”左边的式子不论是“true” 或“false” 都要进右边式子的计算。 - 类似的,“||”和“|”的区别在于:
当“||”左边的结果是“true”时,右边运算式不再进行计算,最终结果是“true”。 而“|”左边的式子不论是“true” 或“false” 都要进右边式子的计算。
Day3
第四小节——switch结构
Java程序有三个基本结构,分别是 顺序结构,选择结构和循环结构。而switch就属于选择结构,由 控制表达式 和多个 case语句 及 一个default语句 构成。
其中的控制表达式的结果只能是byte,short,int,char,enum枚举和String字符串。
switch语句执行过程就是,将表达式的结果按先后顺序与多个case后的目标值进行匹配,一旦匹配成功,就会执行该case后的语句。如果没有遇到 break 和 return,程序会将switch语句执行到底,即一直到执行完default语句。
如下:
······
int a=1;
switch (a){
csae 1: 语句 1;
case 2: 语句 2;
······
case n: 语句 n;
default:语句 n+1;
}
······
程序会从case1开始,一直执行到default;
但在实际应用的时候,我们更希望一种结果对应一个语句,所以多数会用 break语句来结束switch语句。
如下:
······
int a=1;
switch (a){
csae 1: 语句 1;break;
case 2: 语句 2;break;
······
case n: 语句 n; break;
default:语句 n+1; break;
}
······
程序执行完case1后面的语句1后就结束了switch语句,紧接着执行switch后面的代码。
区别
- break 结束的范围是当前选择结构。
- return结束的范围是当前方法。
- 很明显,return 结束的范围比 break 大一级。
Day 5
第五小节——值传递与地址传递
内存条中储存数据的地方有两个,即 堆 和 栈。
普通变量在定义时会在栈上开辟储存空间,在栈上直接储存数据的二进制补码(也就是所谓的值),不会涉及堆;而定义数组时同样会在栈上开辟空间,储存的是堆的地址,而堆储存的是数据的二进制补码(值)。
在同类型数据之间进行传递的时候,传递的是栈或堆的储存内容,当内容是值的时候,就是值传递;若传递的是地址,就叫地址传递。
两个普通变量传值就是简单的赋值操作,当其中一个变量的值发生改变时,不会影响另一个变量;应当注意,数组名储存的就是一个地址。当它赋值给另一个数组名的时候实现了地址的传递,即两个数组类型的变量储存同一个地址,对应堆中的一段内存。当使用一个数组名使堆中的储存内容变化时,另一个数组名相应的堆中内容也发生变化 — — 本质上就是一段内存可以用两个变量来运用。
个人觉得,堆和栈是人为划分的,以帮助学习者理解。
按我的理解,值传递就是数据的直接传递;而地址的引入使一系列的数据变得有序,更加容易被运用或操作。
Day 6
第三课 面向对象(要点)
第一小节——类与对象
Java中,对象又叫实例。我理解为 独立的个体,即定义了一个对象就对应一个独立的个体。
之所以说这个个体独立,是因为赋予了它具体的属性和功能,其中属性是基本数据类型,具体化就是指赋值;其中功能叫做方法,引用时会实现一种功能。当然,功能和方法可以有多个,一般是分开定义,即便是同类型的。
在定义对象之前必须定义类。可以把类看做一个模板,即里面已经定义了一些数据类型和方法。先说定义格式
类名 对象名 = new 类名 ()
例子:
······
public class Student{ //先定义了类
String name;
int age;
public void read()
{
System.out.println ("在读书。")
}
}
public static void main (String[] args) {
//定义对象
Student stu = new Student () // 定义了一个名为 stu 的对象
}
······
由上面的代码可知,类的定义方法
修饰符 class 类名 (){ // 这是最基本的定义方法的形式
//包括类的成员变量和方法
}
类中的属性只是数据类型,并未赋值,它是根据对象的需要而抽象出来的;同样地,方法也是按需定义。
第二小节——方法的重载
同一个类中有多个方法名相同但参数列表不同的方法,这种想象叫做方法的重载,其中参数列表的不同包括以下情形:
1、参数个数的不同
2、参数对应的类型不同
下面看个例子:
······
public class compute {
int add (int num1, num2)
int add (int a,int b)
}
······
上面的两个 add 方法算不算重载?
不算!
因为它只是参数的名不同,而个数和类型都相同!
判断重载时,不作为比较对象的还有:访问控制符、修饰符、返回值类型。
在引用的时候,具体引用哪个方法取决于调用方法传入的参数的数据类型和个数。
Day 7
第三小节——构造方法
什么是构造方法?
简单来说,实现实例化对象的同时对这个对象进行赋值。构造方法是类的一个特殊的成员,在类实例化的同时自动调用。
构造方法的形式:
[修饰符] 方法名 ([参数列表]} {
// 方法体
}
构造方法需要满足以下三个条件:
(1)方法名与类名相同;
(2)在方法名前面没有返回值类型的声明;
(3)在方法中,可以单独写return语句来作为方法的结束,但不可返回一个值。
构造方法一般分为无参和有参两大类,其中无参构造方法在定义对象时会被自动调用,而有参的需要要在定义的同时赋值,才能正常调用。
无参构造方法的例子:
······
class Person{
//构造方法
public Person () {
System.out.println("调用了无参的构造方法");
}
}
public static void main(String[] args) {
Person p = new person();//实例化对象
}
运行结果就是 “调用了无参的构造方法”
有参构造方法的例子:
······
class Person{
int age;
//构造方法
public Person (int a) {
age = a;
}
//定义speak方法
public void speak(){
System.out.println("我今年"+age+"岁了”)
}
}
public static void main(String[] args) {
Person p = new person(18);//实例化对象
p.speak;
}
程序运行结果是 “我今年18岁了”
Day 8
第四章——继承与多态(要点)
第一点 :父类构造方法对子类构造方法的影响
首先应该知道,Java中每个类都至少有一个自己的构造方法,如果在一个类中没有显式地定义构造方法,系统就会自动为这个类创建一个默认的无参构造方法,在其方法体中没有任何代码,即什么也不做。
什么是构造方法?
在类中由系统或人为创建的一个方法,该方法的特点在于名字与类名相同,在调用或实例化对象时会自动执行,且只执行一次,第二次实例化时不再执行。
注意两点:
- 在继承中,子类的构造方法会至少调用父类的一个构造方法,这时可以在子类的构造方法中通过super 关键字指定调用父类的哪一个构造方法。
- 如果没指定,会默认调用父类的无参构造方法。
第二点:重写
继承父类的属性和方法可以大大缩减子类代码的重复率,可父类的某些方法不太适合子类,就需要重写方法。
格式:
[修饰符] 返回值类型 类名 (){
// 方法体
}
三点要求:
(1)父类方法与子类方法的命名相同
(2)子类方法的返回值类型与父类相同
(3)子类的参数列表与父类相同
还有几点要注意
返回值:如果父类方法的返回值类型为引用类型,则子类重写后的方法的返回值类型要么与父类一致,要么子类重写后的方法的返回值类型是父类方法返回值的子类。
修饰符:父类方法final修饰,则子类不允许重写;父类方法由static修饰,该方法可以被继承但不可被重写。子类重写后的方法不允许被static修饰。
访问权限:子类重写以后访问权限要么与父类相同,要么比父类大。
第三点:多态
······
public class Mammal{
//类体
}
······
public class Whale extends Mammal {
//类体
}
·····
public static void main(String[] args){
Mammal mammal = new Whale (); //父类类型的变量=子类创建的对象
mammal.move(); //调用变量的方法——执行时,调用子类的move方法;编译时,调用父类的move方法
}
何为多态?
多态是指不同类的对象在调用同一个方法时所表现出的多种不同的行为状态。通常来说,在一个类中定义的属性和方法被其他类继承或重写,当把子类对象直接赋值给父类引用变量时,相同引用类型的变量调用同一个方法将呈现不同的形态。
注意:
- 多态一定是编译时和运行时不同,且一定伴随着方法重写。
- 多态中子类创建的对象称为上转型对象;上转型对象不能调用子类新增的方法和属性。
- 上转型对象为了调用子类新增的属性和方法,需要下转型。
- 如果子类属性名和父类相同,则上转型对象调用的属性是父类中的属性。
Day 9
特别篇——浅论访问权限
在Java中,共有四种访问权限用来修饰类、方法和常量:public、protected、default及private。
修饰符不同,在什么范围内可以被访问是不同的。
- public:同一工程中所有的类中都可使用。
- protected:在本类和同包中可以被使用,在继承关系的不同包中也可以被使用。
protected修饰的类中,对于非静态的成员只能由子类创建的对象来调用,对于静态的成员,可由子类类名、父类类名、子类对象、父类对象来调用。
- default:在本类和同包中可以被使用
- private:在本类中被使用
为什么要封装?
在实际中,直接操作属性时因其值没有限制,很有可能会没有实际意义。为保证数据的有效性,就需要对属性进行封装。
封装方法:
使用公共方法(起限定作用)操作私有属性(private),保证数据有效性。
Day10
第五章——Java 中的常用类
第一点:String类
赋值方式:一般来说有两种,在初始化的时候直接赋值和用方法创建对象后赋值,具体如下:
······
String s = "hello"; // 在初始化的时候赋值
String st = new String();
st = "Hello"; //在调用String方法后赋值
······
另外需要注意的一点是,创建两个String常量初始化直接赋的值如果相同,那么两个常量在栈中地址相同。
······
String str1 = "hello"; // 在初始化的时候赋值
str2 = "hello"; //再创建一个String型常量
System.out.println(Str1 == Str2);// 验证,在引用类型中,= = 判断地址是否相同
······
输出结果是“true”,Str1与Str2代表同一地址。
而创建对象之后赋值是直接在栈中开拓一个新的空间,每定义一个就开拓一个不同的空间。
第二点:String的 equals() 方法
equals() 的功能是比较两个字符串常量内容或者两个字符串是否相同。
其实,equals() 在object类中功能是比较内存地址是否相同,而在String类中重写了这一方法,变成了比较字符串内容是否相同,返回值为Boolean型。
调用方式:
str1.equals(str2) //str1 和 str2 是两个字符串
第三点:static 修饰
在同一个类中,静态代码块要先于非静态代码块加载,所以静态方法不能直接调用非静态方法或属性,同样地,静态代码块也不能直接调用非静态方法或属性;而非静态代码块或者方法可直接调用静态代码块或方法。
非静态代码块、方法或成员变量会在创建对象的时候完成加载。要想用静态的东西,可以通过创建对象来调用。
总之,静态的先于非静态的,所以静态的不能直接调用非静态的,而非静态的可以直接调用静态的;静态的可以直接调用静态的,非静态的可以直接调用非静态的。
另外,由static 和 final 修饰的的类不能被继承。
第四点:抽象类和接口
抽象类
当定义一些累的时候,常常需要定义一些方法来描述该类的行为特征,但有时这些方法的实现方式是不确定的,就算事先确定了一种实现的方法,由于不同的子类有不同的实现方式,在之后也会被重写;为了使代码更为简洁,引入了抽象类和抽象方法的概念。
抽象类和普通类的区别:
(1)抽象类可以定义抽象方法,也可以不定义;普通类不能定义抽象方法。
(2)抽象类不能被static和final修饰;普通类可以。
(3)抽象类不可以被实例化;普通类可以。
抽象方法:没有结构体的方法叫做抽象方法;
定义格式为
【修饰符】 abstract 返回值类型 方法名(参数列表);
接口
如果一个类中所有方法均是抽象方法,则可以将这个类定义为接口(interface)。
定义格式
[修饰符] interface 返回值类型 (参数列表);
特点:
1、如果一个类不能实现接口中的所有抽象方法,则该类必须是抽象方法。接口将抽象进行得更加彻底;
2、接口中定义抽象方法时,可以省略 public abstract;
3、接口可以继承多个接口,但不能继承类;
4、为了区别于类,接口常以 I 开头
5、JDK8.0版本及其以后的版本中 可以定义多个默认方法和static方法;
6、接口中不能定义构造方法,更不能创建对象;
7、接口中定义的变量为 public static final 修饰的常量,所以名字要大写。多个单词组成时以下划线间隔。
8、JDK8.0 及其以后的版本中,如果一个接口只含有一个抽象方法,则可以定义为函数接口。
对比
抽象类与接口的区别:
- 定义时关键字不同:abstract 、interface;
- 在抽象类中,成员变量为任意合法的成员变量;而在接口中必须是公开静态常量(默认由public static final 修饰)
- 抽象类中有构造方法,接口中没有;
- 抽象类通过自定义类继承抽象类的抽象方法;而接口通过自定义类implements接口来实现抽象方法,定义类可以implements多个接口。
PS:instanceof 关键字
用于判断一个对象是否由某个类,某个父类或某个接口所创建的。
格式
boolean (对象名 instanceof 类名或接口名)
第五点:内部类
内部类根据位置的不同可分为成员内部类和局部内部类
区别
成员内部类直接定义在外部类的里面,方法的外面;而局部内部类定义在外部类的方法体内。
访问权限
- 外部类的访问权限只能是“public”和“default”;
- 成员内部类访问权限可以是任意的;
- 内部类不能有修饰符(因为他是外部类成员的一个组成元素,而访问权限决定的是外部类中的成员能否在其他类中被调用)
内部类的调用
外部类名 变量名 = new 外部类名().new 内部类名();
内部类名 变量名 = new 内部类名();
内部类名 变量名 = 外部类名.new内部类();
匿名内部类
顾名思义就是没有名字的内部类,在调用包含有接口类型的方法时,为了简化代码,不会创建一个接口实现类作为参数传入,而是通过匿名内部类的形式传入一个接口类型参数,在匿名内部类中直接完成方法的实现。
创建格式:
new 父类或接口{
//匿名内部类实现部分
}
特点:
- 匿名内部类一定不是抽象类;
- 匿名内部类不能自定义构造方法,但是可以通过非静态代码块初始化成员变量;
- 匿名内部类匿名内部类一定是接口实现类(该实现类只能实现一个接口)或类(普通类或抽象类)的子类;
- 匿名内部类中可以新增加属性和方法,但是这些属性和方法不能被上转型对象所调用,只能被非上转型对象调用。