1. java基础概念
在 Java 这门语言体系当中,最基础的部分就是 Java SE 部分,Java 的标准版本。它包括Java最基础的一些结构,包括面向对象的一些特性等等,同时它也是 Java 技术基础和核心。
在 Java SE 的基础之上,又分为了 :
Java EE(Java 的企业版),应用于大型企业级应用的开发。
Java ME 主要用于嵌入式开发。
初学的时候我们都是从 Java SE 开始的。
- JVM 叫 Java 虚拟机,它也是整个 Java 技术的核心。Java 语言的跨平台就多亏了 JVM。
- JDK 叫 Java 开发工具包,没有 JDK 就没有办法进行 Java 程序的开发。
- JRE 叫 Java 运行环境,如果我们需要运行一个Java程序,就得安装 JRE。
Java程序基本运行原理如下图:
2. Java 程序的构成
一个Java程序可以认为是一系列对象的集合,而这些对象通过调用彼此的方法来协同工作。
对象(object):对象是类的一个实例,有状态和行为。例如,一条狗是一个对象,它的状态有:颜色、名字、品种;行为有:摇尾巴、叫、吃等。
类(class):类是一个模板,它描述一类对象的行为和状态。
方法(method):方法就是行为,一个类可以有很多方法。逻辑运算、数据修改以及所有动作都是在方法中完成的。
实例变量:每个对象都有独特的实例变量,对象的状态由这些实例变量的值决定。
1、为什么一切都开始于一个类?
Java程序是由类组成,一个类包含方法和属性。这是由于它的面向对象的特征:一切皆对象,每个对象都是一个类的实例。面向对象编程有很多优势,比如更好的模块化,扩展性强等
2、为什么总有一个“main”方法?
“main”方法是程序的入口,它是静态的。 “static”是指该方法是类的一部分,而不是对象的一部分。
这是为什么?我们为什么不把一个非静态方法作为程序的入口? 如果方法不是静态的,那么需要创建一个对象后才能使用方法。因为必须用对象去调用方法。对于程序的入口,这是不现实的。所以,程序的入口方法是静态的。
参数“String[] args”表示一个字符串数组可以被传入到该程序,用来初始化程序。
3. Java 关键字
Java 的关键字对 java 的编译器有特殊的意义,他们用来表示一种数据类型,或者表示程序的结构等,关键字不能用作变量名、方法名、类名、包名。
Java 关键字有如下表所列,目前共有50个Java关键字,其中,"const"和"goto"这两个关键字在Java语言中并没有具体含义。同学们先有个印象,具体含义我们将在后续的内容中详细讲解:
4. Java 标识符
Java 语言中,类、变量、常量、方法都需要名字,我们统统称之为 Java 标识符.
标识符是用来给类、对象、方法、变量、接口和自定义数据类型命名的。
关于 Java 标识符,有几点需要注意的:
1. Java 标识符由数字,字母A-Z或者a-z和下划线_,美元符号$组成。
2. 所有的标识符都应该以字母A-Z或者a-z,美元符$、或者下划线_开始,首位不能是数字。
3. 关键字不能用作标识符。
4. 在 Java 中是区分大小写的。
在 Java 中,还有一些约定俗成的命名规则,希望同学们在写代码的时候都能遵循这些规则:
1. 类和接口名。每个字的首字母大写,含有大小写。例如,MyClass,HelloWorld,Time 等。
2. 方法名。首字符小写,其余的首字母大写,含大小写。尽量少用下划线。例如,myName,setTime 等。这种命名方法叫做驼峰式命名。
3. 常量名。基本数据类型的常量名使用全部大写字母,字与字之间用下划线分隔。对象常量可大小混写。例如,SIZE_NAME。
4. 变量名。可大小写混写,首字符小写,字间分隔符用字的首字母大写。不用下划线,少用美元符号。
5. 命名过程中尽量做到见名知意,方便后期查看和修改代码,也方便其他人员的阅读。
5. 变量
变量(variable)占据一定的内存空间。不同类型的变量占据不同的大小。Java 中的变量类型如下:
Java中主要有如下几种类型的变量:
局部变量
类变量(静态变量)
成员变量(非静态变量)
变量的概念实际上来自于面向过程的编程语言。在Java中,所谓的变量实际上是基本类型 (premitive type).
6. 常量
常量代表程序运行过程中不能改变的值。我们也可以把它们理解为特殊的变量,只是它们在程序的运行过程中是不允许改变的。常量的值是不能被修改的。
常量的语法格式和变量类似,只需要在变量的语法格式前面添加关键字final即可。在Java编码规范中,要求常量名必须大写。
常量的语法格式如下:
final 数据类型 常量名称 = 值;
final 数据类型 常量名称1 = 值1, 常量名称2 = 值2,……常量名称n = 值n;
在Java语法中,常量也可以首先声明,然后再进行赋值,但是只能赋值一次,示例代码如下:
final 用于声明属性(常量),方法和类,分别表示属性一旦被分配内存空间就必须初始化, 它的含义是“这是无法改变的”或者“终态的”。
一般在 Java 里有三种注释:
行注释//:只注释一行
段注释/*...*/:注释若干行
文档注释/**...*/:注释若干行,并写入 javadoc文档
7. 数据类型
- 在Java中有8种基本数据类型:
浮点型:float(4 byte), double(8 byte)
整型:byte(1 byte), short(2 byte), int(4 byte) , long(8 byte)
字符型: char(2 byte)
布尔型: boolean(JVM规范没有明确规定其所占的空间大小,仅规定其只能够取字面值"true"和"false")
对于这8种基本数据类型的变量,变量直接存储的是“值”,因此在用关系操作符==来进行比较时,比较的就是 “值” 本身。要注意浮点型和整型都是有符号类型的,而char是无符号类型的(char类型取值范围为0~2^16-1).
-
非基本数据类型的变量,称作为 引用类型的变量。String str1; str1就是引用类型的变量.
str1= new String("hello");
引用类型的变量存储的并不是 “值”本身,也就是说并不是直接存储的字符串"hello",而是于其指向的对象在内存中的地址。
8. == 和 equals 的区别?
==
(1) 如果作用于基本数据类型的变量,则直接比较其存储的 “值”是否相等;
int a = 2; int b = 2; a == b; // true
(2) 如果作用于引用类型的变量,则比较的是所指向的对象的地址.
String str1 = new String("hello");
String str2 = new String("hello");
str1 == str2 ; // false, str1 和str2分别指向不同的对象,各自存储的是所指向的对象在内存中的地址,并不是“值”本身,也就是说并不是直接存储的字符串"hello",而不同的对象在内存中的地址不同
equals()
而想要比较对象的内容是否相同时,Java 提供了一个特殊的方法equals(),它不适用于基本类型,基本类型使用==和!=进行比较。
(因为equals()默认比较引用)
(1) 对于equals方法,注意:equals方法不能作用于基本数据类型的变量
(2) 如果没有对equals方法进行重写,则比较的是引用类型的变量所指向的对象的地址;(那这么说,这种情况下== 和 equals() 的 比较的都是引用类型的变量的值,即变量所指向的对象在内存中的地址)
(3) 诸如String、Integer、Double、Date等类对equals方法进行了重写的话,比较指向的对象所存储的内容是否相等。
Integer n1 = new Integer("hello");
Integer n2 = new Integer("hello");
n1 == n2
; //false
n1.equals(n2);
// true
9. 自动类型转换和强制类型转换
在Java程序中,不同的数据类型有些时候需要进行相互转换。数据类型转换就分为了自动类型转换和强制类型转换
自动类型转换是在程序执行过程中,不需要我们去特殊声明或者操作,变量由于需要而自动转换成了合适的数据类型。
自动类型转换需要满足下面的两个条件:
目标类型与原类型兼容
目标类型的字节数大于或等于原类型字节数
基本数据类型中,布尔类型boolean占有一个字节,由于其本身所代码的特殊含义,boolean类型与其他基本类型不能进行类型的转换(既不能进行自动类型的提升,也不能强制类型转换), 否则,将编译出错。
强调:
==和!=适用于所有的基本数据类型,其他关系运算符不适用于boolean,因为boolean值只有
true
和false
,比较没有任何意义。==和!=也适用于所有对象,可以比较对象的引用是否相同。
引用:Java 中一切都是对象,但操作的标识符实际是对象的一个引用。
因为两个对象是相同的。尽管两个对象的内容相同,但它们的引用却不相同,==和!=比较的就是对象的引用,所以结果false,再是true,如下:
练习
先想一想下面的逻辑表达是的值是 true 还是 false。然后用代码验证一下吧。
(5 > 2) && (4 == 5)
false || (2 < 5)
提示:C语言中,非0就是true,0就是false,然而这个规则不适用于Java,Java的int不能自动转为boolean。参考while(true){} 而不是while(1){}。
Java里不允许将一个数字作为布尔值使用,虽然这在C和C++是允许的,如果要在布尔测试里使用一个非布尔值,需要先用一个条件表达式将其转换成布尔值
10. 数组
先定义后使用
一维数组
也可以在数组声明的时候初始化数组,或者为它分配好空间
Java中可以将一个数组赋值给另一个数组,如:
修改a2的值,a1的值也跟着变化
二维数组:
11. Java方法
我们经常使用到System.out.println(),它是什么呢?
println() 是一个方法
System 是系统类
out 是标准输出对象
这句语句的意思是调用系统类 System 中的标准输出对象 out 中的方法 println()。
在上面的语法说明中:
(1) 访问修饰符:代表方法允许被访问的权限范围, 可以是 public、protected、private 甚至可以省略 ,其中 public 表示该方法可以被其他任何代码调用,其他几种修饰符的使用我们会在后面章节中详细讲解。
(2) 返回值类型:方法返回值的类型,如果方法不返回任何值,则返回值类型指定为 void (代表无类型);如果方法具有返回值,则需要指定返回值的类型,并且在方法体中使用 return 语句返回值。
(3) 方法名:是方法的名字,必须使用合法的标识符。
(4)参数列表:是传递给方法的参数列表,参数可以有多个,多个参数间以逗号隔开,每个参数由参数类型和参数名组成,以空格隔开。当方法被调用时,传递值给参数。这个值被称为实参或变量。参数列表是指方法的参数类型、顺序和参数的个数。参数是可选的,方法可以不包含任何参数。
(5)方法体:方法体包含具体的语句,定义该方法的功能。
根据方法是否带参、是否带返回值,可将方法分为四类:
无参无返回值方法
无参带返回值方法
带参无返回值方法
带参带返回值方法
在进行方法重载的时候需要遵循以下的规则:
- 在使用方法重载的时候,必须通过方法中不同的参数列表来实现方法的重载。如:方法的参数个数不同或者方法的参数类型不同。
- 不能通过访问权限,返回值类型和抛出的异常来实现重载
- 重载的方法中允许抛出不同的异常
- 可以有不同的返回值类型,只要方法的参数列表不同即可
- 可以有不同的访问修饰符
注意:
1. 如果方法的返回类型为 void ,则方法中不能使用 return 返回值。
2. 方法的返回值最多只能有一个,不能返回多个值。
3. 方法返回值的类型必须兼容,也就是说如果返回值类型为 int ,则不能返回 String 型值。
4. 调用带返回值的方法时,由于方法执行后会返回一个结果,因此在调用带返回值方法时一般都会接收其返回值并进行处理。
在java中重载方法有两种方式:
- 通过改变参数的数量
- 通过更改数据类型
12. 面向对象:(类和对象知识已会)
变量
一个类可以包含以下类型变量:
- 局部变量:在方法、构造方法或者语句块中定义的变量被称为局部变量。变量声明和初始化都是在方法中,方法结束后,变量就会自动销毁。
- 成员变量:成员变量是定义在类中,方法体之外的变量。这种变量在创建对象的时候实例化。成员变量可以被类中方法、构造方法和特定类的语句块访问。
- 类变量:也叫静态变量,类变量也声明在类中,方法体之外,但必须声明为static类型。
注意
1. 局部变量的作用域仅限于定义它的方法内。而成员变量的作用域在整个类内部都是可见的。
2. 同时在相同的方法中,不能有同名的局部变量;在不同的方法中,可以有同名的局部变量。
3. 成员变量和局部变量同名时,局部变量具有更高的优先级。
构造方法
每个类都有构造方法。如果没有显式地为类定义构造方法,Java编译器将会为该类提供一个默认构造方法。
在创建一个对象的时候,至少要调用一个构造方法。构造方法的名称必须与类同名,一个类可以有多个构造方法。
new后面跟的并是构造方法,new + 构造方法可以创建一个新的对象。
1、构造方法的名称与类名相同,且没有返回值
2、如果我们在定义类的时候没有写构造方法,系统会默认给我们生成一个无参构造方法,不过这个构造方法什么也不会做。
3、当有指定的构造方法时,系统都不会再为我们添加无参构造方法了。
4、构造方法的重载:方法名相同,但参数不同的多个方法,调用时会自动根据不同的参数选择相应的方法。
包
为了更好地组织类,Java 提供了包机制,用于区别类名的命名空间。
包的作用?
- 把功能相似或相关的类或接口组织在同一个包中,方便类的查找和使用。
- 包采用了树形目录的存储方式。同一个包中的类名字是不同的,不同的包中的类的名字是可以相同的,当同时调用两个不同包中相同类名的类时,应该加上包名加以区别。
- 包也限定了访问权限,拥有包访问权限的类才能访问某个包中的类。
如何定义一个包?
用package关键字,加上我们的包名。
如何在不同包中使用另一个包中的类?
用到import
关键字: import com.shiyanlou.People
同时如果import com.shiyanlou.*
: 这是将包下的所有文件都导入进来,*是通配符。
注意
包的命名规范是: 全小写字母拼写
面向对象有三大特性: 封装, 继承, 多态
13. java封装
什么是封装?
封装就是把代码的实现细节部分包装起来, 防止该部分代码或者数据被外部随机访问.可以认为是一个保护屏障.
为什么要这么做?
1.只能通过规定的方法访问数据.
2.可以隐藏类的实现细节,提高安全性
如何实现封装?
1.修改属性的可见性,在属性的前面添加修饰符(private)`
2.对每个值属性提供对外的公共方法访问,如创建 getter/setter(取值和赋值) 方法,用于对私有属性的访问
3.在 getter/setter 方法里加入属性的控制语句,例如我们可以加一个判断语句,对于非法输入给予否定。
访问修饰符
访问修饰符可以用来修饰属性和方法的访问范围。
解释:
- private修饰的属性或者方法,只能在当前类中访问或者使用。
- 默认是什么修饰符都不加,默认在当前类中和同一包下都可以访问和使用。
- protected修饰的属性或者方法,对同一包内的类和所有子类可.
- public修饰的属性或者方法,对所有类可见。
example:
package com.shiyanlou;
public class People {
//属性(成员变量)有什么,前面添加了访问修饰符private
//变成了私有属性,必须通过方法调用
private double height; //身高
//属性已经封装好了,如果用户需要调用属性
//必须用getter和setter方法进行调用
//getter和setter方法需要程序员自己定义
public double getHeight(){
//getter 方法命名是get关键字加属性名(属性名首字母大写)
//getter 方法一般是为了得到属性值
return height;
}
//同理设置我们的setter方法
//setter 方法命名是set关键字加属性名(首字母大写)
//setter 方法一般是给属性值赋值,所以有一个参数
public void setHeight(double newHeight){
height = newHeight;
}
}
然后在我们的 main 函数里的对象,不能再直接调用属性了,只能通过getter和setter方法进行调用。
package com.shiyanlou;
public class NewObject {
public static void main(String[] args) {
People LiLei = new People(); //创建了一个People对象LiLei
//利用setter方法为属性赋值
LiLei.setHeight(170.0);
//利用getter方法取属性值
System.out.println("LiLei的身高是"+LiLei.getHeight());
}
}
成员内部类
package com.shiyanlou;
//外部类People
public class People {
private String name = "LiLei"; //外部类的私有属性
//内部类Student
public class Student {
String ID = "20151234"; //内部类的成员属性
//内部类的方法
public void stuInfo(){
System.out.println("访问外部类中的name:" + name);
System.out.println("访问内部类中的ID:" + ID);
}
}
//测试成员内部类
public static void main(String[] args) {
People a = new People(); //创建外部类对象,对象名为a
Student b = a.new Student(); //使用外部类对象创建内部类对象,对象名为b
// 或者为 People.Student b = a.new Student();
b.stuInfo(); //调用内部对象的suInfo方法
}
}
由此,我们可以知道,成员内部类的使用方法:
Student 类相当于 People 类的一个成员变量,所以 Student 类可以使用任意访问修饰符
Student 类在 People 类里,所以访问范围在类里的所有方法均可以访问 People 的属性(即内部类里可以直接访问外部类的方法和属性,反之不行)
定义成员内部类后,必须使用外部类对象来创建内部类对象,即 内部类 对象名 = 外部类对象.new 内部类();
如果外部类和内部类具有相同的成员变量或方法,内部类默认访问自
己的成员变量或方法,如果要访问外部类的成员变量,可以使用 this 关键字.如:a.this
注:成员内部类不能含有static的变量和方法,因为成员内部类需要先创建了外部类,才能创建
它自己的。
14. 继承
为什么需要继承?
减少重复代码,实现复用
继承的特点?
- 子类拥有父类除private以外的所有属性和方法
- 子类可以拥有自己的属性和方法
- 子类可以重写实现父类的方法
- Java 中的继承是单继承,一个类只有一个父类
注:
Java 实现多继承的一个办法是 implements(实现)接口
方法的重写
重写的方法一定要与原父类的方法语法保持一致,比如:
返回值类型
参数类型及个数
方法名都必须一致。
继承的初始化顺序
程序运行的过程中,是先为父类进行初始化,还是先调用的子类进行初始化的呢?
继承的初始化顺序是先初始化父类再初始化子类。
我们根据代码来验证一下。
由此可知,系统先创建了父类对象,再创建了子类对象,先初始化了属性,再调用了构造函数。
注意
final 修饰类,则该类不允许被继承,为最终类
15.Java 多态
Java中多态的实现方式:接口实现,继承实现(对父类进行方法重写,同一个类中进行方法重载)。
多态存在的三个必要条件:
一、要有继承;
二、要有重写;
三、父类引用指向子类对象(向上转型)。
继承实现多态
向上转型的理解
比如:Dog 类是 Animal 类的子类
如果定义了一个指向子类对象的父类引用类型,那么它能够引用父类中定义的所有属性和方法. 但是不能获取只存在于子类的方法和属性。
注:不能使用一个子类的引用去指向父类的对象。
举个继承例子:
package com.shiyanlou;
class Animal {
//父类方法
public void bark() {
System.out.println("动物叫!");
}
}
class Dog extends Animal {
//子类重写父类的bark方法
public void bark() {
System.out.println("汪、汪、汪!");
}
//子类自己的方法
public void dogType() {
System.out.println("这是什么品种的狗?");
}
}
public class Test {
public static void main(String[] args) {
Animal a = new Animal();
Animal b = new Dog();
Dog d = new Dog();
a.bark();
b.bark();
//b.dogType();
//b.dogType()编译不通过
d.bark();
d.dogType();
}
}
在这里,由于b是父类的引用,指向子类的对象,因此不能获取子类的方法(dogType()方法),同时当调用bark()方法时,由于子类重写了父类的bark()方法,所以调用子类中的bark()方法。
因此,向上转型,在运行时,会遗忘子类对象中与父类对象中不同的方法,也会覆盖与父类中相同的方法——重写。(方法名,参数都相同)
接口实现继承
interface
关键字定义接口,它会产生一个完全抽象类,根本没有提供任何方法体。
多继承实现方式:
实现上面的接口:
注意点:
- 接口不能用于实例化对象
- 接口中所有的方法是抽象方法
- 接口成员是 static final 类型
- 接口支持多继承
abstract: 抽象类和抽象方法
Java提供了一个叫做抽象方法的机制,这种方法是不完整的,仅有声明而没有方法体。而包含抽象方法的类叫做抽象类,抽象类在定义类时,前面会加abstract关键字。
那我们什么时候会用到抽象类呢?
在某些情况下,某个父类只是知道其子类应该包含怎样的方法,但无法准确知道这些子类如何实现这些方法。也就是说抽象类是约束子类必须要实现哪些方法,而并不关注方法如何去实现。
从多个具有相同特征的类中抽象出一个抽象类,以这个抽象类作为子类的模板,从而避免了子类设计的随意性。
那抽象类如何用代码实现呢,它的规则如下:
- 用 abstract 修饰符定义抽象类
- 用 abstract 修饰符定义抽象方法,只用声明,不需要实现
- 包含抽象方法的类就是抽象类
- 抽象类中可以包含普通的方法,也可以没有抽象方法
- 抽象类的对象不能直接创建,我们通常是定义引用变量指向子类对象。
举个抽象类的例子
package com.shiyanlou;
//抽象方法
public abstract class TelePhone {
public abstract void call(); //抽象方法,打电话
public abstract void message(); //抽象方法,发短信
}
构建子类,并实现抽象方法。
package com.shiyanlou;
public class CellPhone extends TelePhone {
@Override
public void call() {
// TODO Auto-generated method stub
System.out.println("我可以打电话!");
}
@Override
public void message() {
// TODO Auto-generated method stub
System.out.println("我可以发短信!");
}
public static void main(String[] args) {
CellPhone cp = new CellPhone();
cp.call();
cp.message();
}
}
static
1.声明为static的变量实质上就是全局变量(静态变量=类变量),当声明一个对象时,并不产生static变量的拷贝,而是该类所有的实例变量共用同一个static变量。
访问语法:
类名.静态变量名
通常在以下两个功能时使用静态变量:
- 在对象之间共享值时
- 方便访问变量时
2. 用static修饰的方法,叫做静态方法---可以直接通过类名来访问,访问语法为:
类名.静态方法名(参数列表...)
静态方法有以下几个限制:
- 它们仅能调用其他的static 方法。
- 它们只能访问static数据。
- 它们不能以任何方式引用this 或super。
总之:类的实例可以通过类名直接访问所有的 静态变量
和 静态方法
.
final
final关键字可以修饰类、方法、属性和变量
- final 修饰类,则该类不允许被继承,为最终类
- final 修饰方法,则该方法不允许被覆盖(重写)
- final 修饰属性:则该类的属性不会进行隐式的初始化(类的初始化属性必须有值)或在构造方法中赋值(但只能选其一)
- final 修饰变量,则该变量的值只能赋一次值,即变为常量
super
super关键字在子类内部使用,代表父类对象。
访问父类的属性 super.属性名
访问父类的方法 super.bark()
子类构造方法需要调用父类的构造方法时,在子类的构造方法体里最前面的位置:super()