数据类型
基础数据类型
1、整数类型 byte,short,int,long
2、浮点类型 float,double
3、字符类型 char
4、布尔类型 boolean
引用数据类型
引用类型的变量类似于C语言的指针,它内部存储一个“地址”,指向某个对象在内存的位置
5、字符串类型 string
6、空值 null 引用类型的变量可以指向一个空值null
,它表示不存在,即该变量不指向任何对象
7、数组类型
8、ArrayList 可变长度”的数组
泛型
泛型就是定义一种模板,例如ArrayList<T>
,然后在代码中为用到的类创建对应的ArrayList<类型>
ArrayList<String> strList = new ArrayList<String>();
java中的集合类型
List
:一种有序列表的集合,例如,按索引排列的Student
的List
;Set
:一种保证没有重复元素的集合,例如,所有无重复名称的Student
的Set
;Map
:一种通过键值(key-value)查找的映射表集合,例如,根据Student
的name
查找对应Student
的Map
。
Java访问集合总是通过统一的方式——迭代器(Iterator)来实现,它最明显的好处在于无需知道集合内部元素是按什么方式存储的
由于Java的集合设计非常久远,中间经历过大规模改进,我们要注意到有一小部分集合类是遗留类,不应该继续使用:
Hashtable
:一种线程安全的Map
实现;Vector
:一种线程安全的List
实现;Stack
:基于Vector
实现的LIFO
的栈。
变量和常量
Java 中主要有如下几种类型的变量
局部变量
类变量(静态变量)
成员变量(非静态变量)
变量定义
在Java中,变量必须先定义后使用,在定义变量的时候,可以给它一个初始值。
int x = 1;
有时变量类型特别长,写起来特别复杂,可以使用var
关键字
StringBuilder sb = new StringBuilder();
var sb = new StringBuilder(); //编译器会根据赋值语句自动推断出变量sb的类型是StringBuilder
常量定义
定义变量的时候,如果加上final
修饰符,这个变量就变成了常量:
final double PI = 3.14; // PI是一个常量
类常量定义
类常量是使得在其他类中可以通过类名直接访问的常量,通过static final
修饰
public class System
{
public static final PrintStream out = . . .;
}
类与对象
对于java而言,所有的变量,函数、方法都是在一个类中定义的,所以下面所有都是指定义类中的,如方法,main函数,初始化块,构造器等
静态域
对于一个类来说,有独属于自己的域,即不属于任何由这个类新建出来的对象,而是由这些对象共享,对于由某个类新建出来的对象,都会拷贝一份类中定义的实例域,但对于类中定义的静态域由所有对象共享。**指属于类但不属于类对象的变量和函数。**对于静态域的变量或函数都不需要通过对象调用,可直接使用类名调用
对于静态变量,用static
修饰。可以理解为共享变量,只要一个对象对齐修改了,则该类派生来的其他对象读取的这个值就是修改后的。
静态变量用的比较少,一般使用静态常量,即可以理解为不可被任何对象修改的全局常量。用static final
修饰
静态方法
类中的静态方法是一种不能操作由该类派生来的对象中的数据。即不能向对象实施操作的方法,不能修改实和访问例域,如下getNextld静态方法,是不能访问Id 实例域的,只能访问静态域数据nextld
class Employee {
private static int nextld = 1;
private int id;
public static int getNextld{
return nextld; // returns static field
}
}
// 可以通过名调用该方法:
int n = Employee.getNextldO ;
一般在下面种情况下使用静态方法:
- 一方法不需要访问对象状态,其所需参数都是通过显式参数提供(例如: Math.pow)
- 一个方法只需要访问类的静态域(例如:Employee.getNextldh)
main方法
因为对于static修饰的函数都不需要通过对象来调用,可直接通过类名调用,java中的main方法就是一个静态方法,main方法不对任何对象进行操作,在启动一个项目的程序时,最开始没有任何一个对象,静态方法main执行并创建程序其他的对象。
public class Application
{
public static void main(StringD args)
{
// construct objects here
}
}
方法参数
在使用和调用方法时,一般都会向方法传递它们需要的参数,参数传递有两种方式,
- 按值传递:这种传递方式是将参数的值拷贝,即方法接收的是该参数的值
- 按引用传递:这种方式是将参数的地址传给函数,即方法接受的是该参数变量在内存中地址
按值传递不会修改原来参数的值,按引用传递则会修改参数原来的值。java中参数传递都是值传递
初始化块
类的初始化块会类构造类时执行,对象构造先执行初始化快,然后再运行构造器的主体部分,这种机制不是必需的,通常会直接将初始化快的代码放在构造器中。
构造器
对于对象的构造,有时需要在对象被构造出来时就可以初始化一些数据,这可借助类中的构造方法实现,构造器名字定义同类名一致,一个类中可定义多个构造器,根据对象构造时调用的具体构造器来实现对象的初始化。
class Employee
{
private static int nextld;
private int id = assignldO;
public Employee(){ //类构造器
name =
salary = 0;
hireDay = LocalDate,now();
}
private static int assignld(){
int r = nextld;
nextld++;
}
}
调用一个和构造器来构建一个对象时的具体步骤:
1、先初始化所有数据域为默认值(0、false、null)
2、按照在类声明中出现的次序,依次执行所有域初始化语句和初始化块
3、如果构造器第一行调用了第二个构造器,则执行第二个构造器主体,依次类推
4、执行本构造器主体
可以在初始化块中先获取一些数据域初始化需要的数据,例如发送请求获取某些数据,再构造一个对象
内部类
内部类是定义在一个类内部的一个类,为什么需要内部类呢:
1、内部类的方法可以访问该类定义所在作用域的数据,包过私有的数据
2、内部类可以对同一个包中的其他类隐藏起来
3、当想要定义一个回调函数且不想编写大量代码时,使用匿名内部类比较便携
泛型
泛型程序设计意味着编写的代码可以被很多不同类型的对象所重用,比如一个ArrayList类可以聚集任何类型的对象,这是一个泛型程序设计实例,
类型参数
比如对于ArrayList可以储存任何类型的数据,但当取出时需要进行类型转换成对应的类型,但事先并不知道数组中存了哪些数据,因此可以在定义时,指定一个类型参数,指定存储什么类型的数据,例如:
ArrayList<String> files = new ArrayList<String>()
或者
ArrayList<String> files = new ArrayListoO<>()
省略的类型可以从变量的类型推断得出
编译器就可以利用这个信息,当调用get的时候,不需要进行强制类型转换,编译器就知道返回类型了,编译器也可以进行检查,避免插入错误的类型对象。
类型参数的魅力在于:使得程序具有更好的可读性和安全性
定义一个泛型类型
public class Pair<T>{
private T first;
private T second;
public Pair() { first = null ; second = null ; }
public PairfT first , T second) { this,first = first; this.second = second; }
public T getFirstO { return first; }
public T getSecondO { return second; }
public void setFirst (T newValue) { first = newValue; }
public void setSecond(T newValue) { second = newValue; }
}
Pair 类引人了一个类型变量T,用尖括号( < >) 括起来,并放在类名的后面,泛型类可以有多个类型变量。例如, 可以定义Pair 类,其中第一个域和第二个域使用不同的类型:
public class Pair<T, U> { . . . }
注:
类型变量使用大写的形式,且比较短。在java库中,使用变量E表示集合的元素类型,K,V分别表示表的关键自与值的类型。T表示“任意类型”。
用具体的类型替换类型变量就可以实例化泛型类型, 例如:
Pair<String>
可以将结果想象成带有构造器的普通类:
Pair<String>0
Pair<String>(String, String)
和方法:
String getFirstO
String getSecond()
void setFirst (String)
void setSecond(Strin=g)
换句话说,泛型类可看作普通类的工厂。
包
使用包中的类
类可以放置在不同的包中,只要包名不冲突,包和包中的类是隔离的,不会产生冲突,即使两个包中放置了两个一模一样的类,而且从编译的角度看,嵌套的包之间没有任何关系,例如ava.util 包与java.util.jar 包毫无关系。每一个都拥有独立的类集合
一个类可以使用所属包中的所有的类,以及其他包中的共有的类(public class)可以用两种方式导入其他包中的公有的类
1、在每个类名之前添加完整的包名
java.tiie.LocalDate today = java.tine.Local Date.now() ;
2、使用import语句导人一个特定的类或者整个包
import java.util .*;
LocalDate today = LocalDate.now();
在C++ 中, 与包机制类似的是命名空间(namespace)。在Java 中, package 与import 语句类似于C+H■ 中的namespace 和using 指令。
将类放置包中
要想将一个类放人包中, 就必须将包的名字放在源文件的开头, 包中定义类的代码之前。例如,
package com.hostname.corejava;
public class Employee{
....
}
如果没有在源文件中放置package 语句, 这个源文件中的类就被放置在一个默认包( defaulf package ) 中。默认包是一个没2有名字的包。
类路径
类文件可以放在一个系统中某个目录中,也可以通过将多个类文件打包成一个JAR文件中,通过在系统变量中设置CLASSPATH
变量指定类文件存放地址,
设置类路径:
1、通过系统变量
CLASSPATH=c:\classdir;.;c:\archives\archive.jar
句点(.)表示当前目录
2、通过命令行
export CLASSPATH=c:\classdir;.;c:\archives\archive.jar
或者
java -classpath c:\classdir;.;c:\archives\archive.jar
**注意:**javac 编译器总是在当前的目录中查找文件, 但Java 虚拟机仅在类路径中有目录的时候才查看当前目录如果没有设置类路径, 那也并不会产生什么问题, 默认的类路径包含目录. 然而如果设置了类路径却忘记了包含目录, 则程序仍然可以通过编译, 但不能运行。
类的特性
1、封装
2、继承
3、多态(通过接口实现)
继承
利用继承,可以基于一个已经存在的类构造一个新的类。继承已经存在的类就是复用这些类的方法和域,在这个基础上可以添加新的方法和域
继承某个已存在的类而产生的新类称为子类或派生类,而被继承的类,即先前存在的类称为父类或超类或基类
对于超类中定义的私有域,子类是无权限访问的,子类可以通过超类中定义的方法来获取超类中私有域的数据,来看下下面的例子,有两个类,一个Employee类,一个Manager类,Manager类继承了Employee类,并有额外的bonus域和setBonos方法
- Employee类
class Employee{
// instance fields
private String name ;
private double salary;
private Local Date hireDay;
// constructor
public Employee(St ring n , double s, int year , int month , int day){
name = n;
salary = s;
hireDay = Local Date.of (year , month, day) ;
}
// a method
public String getName(){
return name;
}
// a method
public double getSalary(){
return salary;
}
}
- Manager类
public class Manager extends Employee{
private double bonus;
// 构造器
public Manager(String name, double salary, int year, int month, int day){
super(name, salary, year , month, day) ;
bonus = 0;
}
public void setBonos(double bonus){
this.bonus = bonus;
}
}
如上用到两个关键字extends和super
1、extends用于指定继承那个类
2、super用于调用父类某个构造器方法,如果不使用在初始化该类时,也会调用父类参数为空的构造器方法
方法覆盖
对于Manager,获取的salary由bonus和salary组成,因此父类中获取salary的方法不适用子类,子类需要重写该方法,如下:
public class Manager extends Employee{
private double bonus;
public void setBonos(double bonus){
this.bonus = bonus;
}
public double getSalary(){
return salary+bonus; //会报错
}
}
因为子类是不能直接访问父类中私有域的,虽然子类中有一个salary的域,但子类不能直接访问salary域,该私有域只能是具体类的方法才能访问,因此只能Employee类的getSalary方法能访问,因此可以改为如下形式:
public class Manager extends Employee{
private double bonus;
public void setBonos(double bonus){
this.bonus = bonus;
}
public double getSalary(){
double baseSalary = getSalaryO; //会报错
return baseSalary + bonus;
}
}
如果直接改为以上的形式然会报错,因为Manager类中就有getSalary方法,这会导致,不断的循环调用该方法直至程序崩溃,这个 时候需要指定调用父类的getSalary方法,可用到关键字super,修改如下
public class Manager extends Employee{
private double bonus;
public void setBonos(double bonus){
this.bonus = bonus;
}
public double getSalary(){
double baseSalary = super.getSalaryO;
return baseSalary + bonus;
}
}
阻止继承
有时候希望某个类不能被任何类继承,可以利用关键字final来修饰该类,称为final类,如下该类继承了Manager类,但自己不能被其他任何类继承,final类中所有的方法自动成为final方法。只对方法自动变成final不包过类中的域。
将方法或类声明为final 主要目的是: 确保它们不会在子类中改变语义。
public final class Executive extends Manager{
}
强制类型转换
Manager boss = new Manager(. . .);
Employee[] staff = new Employee[3];
staff[0] = boss ;
Manager boss = (Manager) staff[0];
Manager boss = (Manager) staff[1] ; // Error
进行判断再转换,防止程序报错退出
if (staff[1] instanceof Manager)
{
boss = (Manager) staff[1]
}
在Java 中, 每个对象变量都属于一个类型。类型描述了这个变量所引用的以及能够引用的对象类型。例如,staff[i] 引用一个Employee 对象(因此它还可以引用Manager对象)。
将一个值存人变量时, 编译器将检查是否允许该操作。将一个了-类的引用赋给一个超类变量, 编译器是允许的。但将一个超类的引用赋给一个子类变量, 必须进行类型转换, 这样才能够通过运行时的检査。
Java修饰符
像其他语言一样,Java可以使用修饰符来修饰类中方法和属性。主要有两类修饰符:
访问控制修饰符 : default, public , protected, private
- 仅对本类可见private。
- 对所有类可见public
- 对本包和所有子类可见protected。
- 对本包可见—默认(很遗憾),不需要修饰符。
一般对于类中的域标记为private,而方法标记为public,任何声明为private的内容对其他类都是不可直接访问的,即使是子类也是不能访问父类中的私有域,但有时候i子类希望访问父类中的方法或某个域,因此可以将这些方法或域声明为protected。
在实际应用中,要谨慎使用protected 属性。假设需要将设计的类提供给其他程序员使用, 而在这个类中设置了一些受保护域, 由于其他程序员可以由这个类再派生出新类,并访问其中的受保护域。在这种情况下, 如果需要对这个类的实现进行修改, 就必须通知所有使用这个类的程序员。这违背了OOP 提倡的数据封装原则。
非访问控制修饰符 : final, abstract, static, synchronized
所有类的超类 Object
Object 类是Java 中所有类的始祖, 在Java 中每个类都是由它扩展而来的。但是并不需要这样写:
public class Employee extends Object
可以使用Object 类型的变量引用任何类型的对象:
Object obj = new Employee('Harry Hacker", 35000) ;
当然, Object 类型的变量只能用于作为各种值的通用持有者。要想对其中的内容进行具体的操作, 还需要清楚对象的原始类型, 并进行相应的类型转换:
Employee e = (Employee) obj ;
在Java 中, 只有基本类型不是对象, 例如, 数值、字符和布尔类型的值都不是对象。所有的数组类型,不管是对象数组还是基本类型的数组都扩展了Object 类
Object中的一些方法
因为所有类都继承了Object,所以Object中的方法在其他类中可以直接使用,Object类中有如下一些方法
1、equals 方法用于检测一个对象是否等于另外一个对象
2、getClass 方法将返回一个对象所属的类
3、hashCode 方法是由对象导出的一个整型值,散列码是没有规律的
4、toString 方法用于返回表示对象值的字符串
反射
java中的反射库提供了一些功能强大的类,可以借助其分析类能力,具体可用来:
1、在程序运行时分析类的能力
2、在运行时查看对象,例如编写一个tostring方法供所有类使用
3、实现通用的数组操作代码
4、利用Method对象,这个对象很像C++的函数指针
Java 标识符
Java 所有的组成部分都需要名字。类名、变量名以及方法名都被称为标识符。
关于 Java 标识符,有以下几点需要注意:
- 所有的标识符都应该以字母(A-Z 或者 a-z),美元符($)、或者下划线(_)开始
- 首字符之后可以是字母(A-Z 或者 a-z),美元符($)、下划线(_)或数字的任何字符组合
- 关键字不能用作标识符
- 标识符是大小写敏感的
- 合法标识符举例:age、$salary、_value、__1_value
- 非法标识符举例:123abc、-salary
java关键字
访问控制 | |
---|---|
private | 私有的 |
protected | 受保护的 |
public | 公共的 |
default | 默认 |
类、方法和变量修饰符 | |
---|---|
abstract | 声明抽象 |
class | 类 |
extends | 扩充,继承 |
final | 最终值,不可改变的 |
implements | 实现(接口) |
interface | 接口 |
native | 本地,原生方法(非 Java 实现) |
new | 新,创建 |
static | 静态 |
strictfp | 严格,精准 |
synchronized | 线程,同步 |
transient | 短暂 |
volatile | 易失 |
程序控制语句 | |
---|---|
break | 跳出循环 |
case | 定义一个值以供 switch 选择 |
continue | 继续 |
default | 默认 |
do | 运行 |
else | 否则 |
for | 循环 |
if | 如果 |
instanceof | 实例 |
return | 返回 |
switch | 根据值选择执行 |
while | 循环 |
错误处理 | |
---|---|
assert | 断言表达式是否为真 |
catch | 捕捉异常 |
finally | 有没有异常都执行 |
throw | 抛出一个异常对象 |
throws | 声明一个异常可能被抛出 |
try | 捕获异常 |
包相关 | |
---|---|
import | 引入 |
package | 包 |
基本类型 | |
---|---|
boolean | 布尔型 |
byte | 字节型 |
char | 字符型 |
double | 双精度浮点 |
float | 单精度浮点 |
int | 整型 |
long | 长整型 |
short | 短整型 |
变量引用 | |
---|---|
super | 父类,超类 |
this | 本类 |
void | 无返回值 |
保留关键字 | |
---|---|
goto | 是关键字,但不能使用 |
const | 是关键字,但不能使用 |
null | 空 |