Java基础
Java开发环境
- Java是一门跨平台语言,它编写的软件可以运行在各种系统上(c++则不然),跨平台特性实现的核心是JVM(Java Virtual Machine,Java虚拟机)
- JVM是运行所有java程序的假象计算机,是java程序的运行环境,用Java编写的程序都运行在JVM之上
- JVM本身不具备跨平台功能,每个操作系统下都有不同版本的JVM,JVM可以简单理解为就是实现翻译功能
- JRE(Java Runtime Environment):是Java程序的运行时环境,包括JVM和运行时需要的核心类库
- JDK(Java Development Kit):是JAVA程序开发工具包,包含JRE和开发人员使用的工具
- 想要运行一个Java程序,只需安装JRE
- 而想要开发一个全新的Java程序,那么需要安装JDK
基本数据类型
- 包含8种基本数据类型,其中包含4种整型
int
(4字节),short
(2字节),long
(8字节),byte
(1字节),2种浮点类型float
(4字节), double(8字节),一种字符类型char
(1字节)表示Unicode编码的代码单元和一种用于表示真假的boolean
类型 - Java还有一个能够表示任意精度的算术包,通常成为“大数”(big number),当他并不是一种基本Java类型,而是一个Java对象
- Java所有数值类型所占据的字节数和范围与运行Java的机器无关,这与C/C++不同,C/C++中,int,long等类型的大小与目标平台相关
- Java没有任何无符号(
unsigned
)类型
Java包(package)基础概念
包的作用
- 1、把功能相似或相关的类或接口组织在同一个包中,方便类的查找和使用
- 2、如同文件夹一样,包也采用了树形目录的存储方式。同一个包中的类名字是不同的,不同的包中的类的名字是可以相同的,当同时调用两个不同包中相同类名的类时,应该加上包名加以区别。因此,包可以避免名字冲突
- 3、包也限定了访问权限,拥有包访问权限的类才能访问某个包中的类
- Java 使用包(package)这种机制是为了防止命名冲突,访问控制,提供搜索和定位类(class)、接口、枚举(enumerations)和注释(annotation)等
包的语法
package pkg1[.pkg2[.pkg3…]];
例如,一个Something.java 文件它的内容
package net.java.util;
- 那么它的路径应该是
net/java/util/Something.java
这样保存的。 package(包) 的作用是把不同的 java 程序分类保存,更方便的被其他 java 程序调用 - 一个包(package)可以定义为一组相互联系的类型(类、接口、枚举和注释),为这些类型提供访问保护和命名空间管理的功能。
- 以下是一些 Java 中的包:
java.lang
-打包基础的类java.io
-包含输入输出功能的函数
- 开发者可以自己把一组类和接口等打包,并定义自己的包。而且在实际开发中这样做是值得提倡的,当你自己完成类的实现之后,将相关的类分组,可以让其他的编程者更容易地确定哪些类、接口、枚举和注释等是相关的
- 由于包创建了新的命名空间(namespace),所以不会跟其他包中的任何名字产生命名冲突。使用包这种机制,更容易实现访问控制,并且让定位相关类更加简单
包的创建
- 创建包的时候,你需要为这个包取一个合适的名字。之后,如果其他的一个源文件包含了这个包提供的类、接口、枚举或者注释类型的时候,都必须将这个包的声明放在这个源文件的开头
- 包声明应该在源文件的第一行,每个源文件只能有一个包声明,这个文件中的每个类型都应用于它
- 如果一个源文件中没有使用包声明,那么其中的类,函数,枚举,注释等将被放在一个无名的包(unnamed package)中
- 例子
- 创建一个包含接口
Animal
的包
// file: Animal.java package animals; // 声明包,表示此文件数据animals包 interface Animal{ public void eat(){} public void travel(){} }
- 在同一个包中加入该接口的实现
package animals; // 声明包,表示此文件数据animals包 /* file : MammalInt.java */ public class MammalInt implements Animal{ public void eat(){ System.out.println("Mammal eats"); } public void travel(){ System.out.println("Mammal travels"); } public int noOfLegs(){ return 0; } public static void main(String args[]){ MammalInt m = new MammalInt(); m.eat(); m.travel(); } }
- 创建一个包含接口
使用包
- 为了能够使用某一个包的成员,我们需要在 Java 程序中明确导入该包。使用 “import” 语句可完成此功能
- 在 java 源文件中 import 语句应位于 package 语句之后,所有类的定义之前,可以没有,也可以有多条,其语法格式为:
import package1[.package2…].(classname|*);
- 在某个类使用同一个包中的类,不需要
import
- 在某个类中引用另一个包的类,需要
import
:- 用
import
关键字引入,使用通配符 “*”,引用包中所有类import packageName.*;
- 使用
import
关键字引入某个类:import packageName.className;
- 用
Java与C++不同点
- Java为解释性语言,其运行过程为:程序源代码经过Java编译器编译成字节码,然后由JVM解释执行。而C/C++为编译型语言,源代码经过编译和链接后生成可执行的二进制代码,可直接执行。因此Java的执行速度比C/C++慢,但Java能够跨平台执行,C/C++不能
- Java是完全面向对象语言,所有代码(包括函数、变量)必须在类中实现,除基本数据类型(包括int、float等)外,所有类型都是类。此外,Java语言中不存在全局变量或者全局函数,而C++兼具面向过程和面向对象编程的特点,可以定义全局变量和全局函数
- Java的所有代码都必须包含在类中
- Java 中,只有类定义,没有类声明,且结尾不需要分号
- Java中没有作用域范围运算符“::”,Java用包代替了C++的命名空间
- Java的定义类时必须在类名前加上访问修饰符
- Java的所有方法和字段的声明都必须在前面加上访问修饰符
- Java的main函数必须用public修饰且是static的
- Java具有平台无关性,即对每种数据类型都分配固定长度的空间,例如int型总是占据32位;而C/C++不然,同一个数据类型在不同平台上会分配不同的字节数
- C/C++中,结构和联合的所有成员均为公有,这往往会导致安全性问题的发生;而Java根本就不包含结构和联合,所有内容都封装在类里面
- C++语言支持预处理;Java没有预处理器,虽然不支持与处理功能(包括头文件、宏定义等),但它提供的import机制与C++的预处理器功能类似
- C++支持默认函数参数;Java不支持默认函数参数
- C/C++支持goto语句;Java不提供goto语句,但Java中的goto是保留关键字
- C/C++支持自动强制类型转换,这会导致程序的不安全;Java不支持自动强制类型转换,必须由开发人员显式地进行强制类型转换
- Java丢弃了C++ 中很少使用的、很难理解的、令人迷惑的那些特性,如操作符重载、多继承、自动的强制类型转换
- Java语言不使用指针,并提供了自动的废料收集,在Java语言中,内存的分配和回收都是自动进行的,程序员无须考虑内存碎片的问题
- Java用接口(Interface)技术取代C++程序中的多继承性。接口与多继承有同样的功能,但是省却了多继承在实现和维护上的复杂性
- Java有反射机制,允许程序在运行时进行自我检查,同时也允许对其内部的成员进行操作。C++没有提供这样的特性
Java数组初始化方式
- 动态初始化
int [] array = new int[10];
- 静态初始化
int [] array = new int[]{1,2,3,4,5}; // 静态初始化的省略格式 int [] array = {1,2,3,4,5};
- 动态和静态初始化分两步走
// 动态两步 int [] arrayA; arrayA = new int [10]; // 静态两步 int [] arrayB; arrayB = new int [] {1,2,3,4,5}; // error, 省略格式不能分两步 /* int [] arrayC; arrayC = {1,2,3,4,5}; */
- 获取数组长度
//格式:arrayname.length int [] array = new int [10]; int len = array.length; // 10
- Java使用动态初始化数组时,数组中的元素都会自动拥有一个默认值
- 若为int类型,默认值为
0
- 若为浮点类型,默认值为
0.0
- 若为字符类型,默认值为
/u0000
- 若为布尔类型,默认值为
false
- 若为引用类型,默认值为
null
- 若为int类型,默认值为
- Java数组特性
- 由于数组类型是Java中的引用类型,所以与c++不同。c++中,数组名是一个指针常量,是不可赋值的。而Java中数组名是一个引用,是指向内存中的地址,可以将数组名引用另一块地址。
int [] array = new int [10]; array = new int [5]; // 引用另一块地址
Java引用类型
- 引用类型是一个对象类型,它的值是指向内存空间的引用,就是地址,所指向的内存中保存着变量所表示的一个值或一组值(数组)
- Java 提供两种不同的类型:引用类型和基本类型(或内置类型),简单地说除了8中基本数据类型,其他都是引用类型
- 引用类型和基本类型的行为完全不同,并且它们具有不同的语义
- 引用类型和原始类型具有不同的特征和用法,它们包括:大小和速度问题,这种类型以哪种类型的数据结构存储,当引用类型和原始类型用作某个类的实例数据时所指定的缺省值
- 对象引用实例变量的缺省值为 null,而基本类型实例变量的缺省值与它们的类型有关。同时为了面向对象操作的一致性
- 这些基本类型都有相应的封装引用类型:Integer、Short、Long、Byte、Float、Double、Boolean、Character等
- Java有4种引用类型:分别为强引用(StrongReference),软引用(SoftReference),弱引用(WeakReference)以及虚引用(PhantomReference),他们被GC回收的可能性从大到小排列
匿名对象
- 由于Java具有自动的内存回收机制,所以对于只使用一次的对象,一般创建一个匿名对象然后调用该匿名对象的方法实现某些功能
- 也可以使用匿名对象来作为参数进行函数传参和作为函数的返回值
- 匿名对象只能使用一次
new ClassName().CallMethod(); // 调用一次匿名对象的方法 CallMethod(new ClassName()); // 匿名对象传参 return new ClassName(); // 匿名作为返回值
- C++中使用new创建的动态对象需要手动delete释放,所以不可使用匿名对象,否则造成内存泄漏
Java访问修饰符
-
private
- 1.在当前类开发中,main方法之外可以直接借助名字使用,当前类的main方法中可以使用对象打点的方式直接使用成员
- 2.在当前类之外,使用对象(或是类名,针对静态的)打点调用都是被限制的。可以通过在当前类中开发一个公有的方法(getter,setter),在公有方法中可以使用这个私有的成员,从而达到间接使用私有成员
- 在继承中,私有成员禁止被继承,也就是说在子类的开发中,拒绝直接使用私有成员的名字进行使用
-
public
- 1.在当前类开发中,main方法之外可以直接借助名字使用,当前类的main方法中可以使用对象打点的方式直接使用成员
- 2.在当前类之外,使用对象(或是类名,针对静态的)打点调用是被允许的
- 3.在子类中,公有成员允许被继承,也就是说在子类的开发中,可以直接使用公有成员的名字进行使用
-
protected
- 同一个包的情况:
- 1.在当前类开发中,main方法之外可以直接借助名字使用,当前类的main方法中可以使用对象打点的方式直接使用成员
- 2.在当前类之外,同一个包中,使用对象(或是类名,针对静态的)打点调用是被允许的
- 3.在子类中,同一个包中,受保护成员允许被继承,也就是说在子类的开发中,可以直接使用受保护成员的名字进行使用。子类的main方法中可以使用子类或是父类对象打点调用
- 4.在当前类之外,子类之外,要使用子类的类中,同一个包中,可以使用子类或是父类对象打点调用。
- 不同包的情况:
- 1.在当前类之外,不同包中,使用对象(或是类名,针对静态的)打点调用是被禁止的
- 2.在子类中,不同包中,受保护成员允许被继承,也就是说在子类的开发中,可以直接使用受保护成员的名字进行使用。子类的main方法中只能通过子类对象打点调用。父类对象不行,这个时候相当于父类中的受保护成员跨包了
- 3.在当前类之外,子类之外,要使用子类的类中,与子类同一个包中,通过子类对象打点是调用不出父类中的受保护的成员的。父类对象更不行,这个时候相当于父类中的受保护成员跨包了。如果想要通过子类对象调用父类的受保护的成员,需要重新覆写父类的这个成员
- 4.在当前类之外,子类之外,要使用子类的类中,与父类同一个包中,通过子类对象打点是可以调用父类中的受保护的成员的。父类对象也行,同个包中
- 5.在与父类和子类都不同包的第三个包中,要想通过子类对象调用父类的受保护成员,可以通过覆写父类受保护成员,并将访问限制符修改成public
- 同一个包的情况:
-
总结
Java继承
继承特性
- 子类拥有父类非 private 的属性、方法
- 子类可以拥有自己的属性和方法,即子类可以对父类进行扩展
- 子类可以用自己的方式实现父类的方法
- Java 的继承是单继承,但是可以多重继承,单继承就是一个子类只能继承一个父类,多重继承就是,例如 B 类继承 A 类,C 类继承 B 类,所以按照关系就是 B 类是 C 类的父类,A 类是 B 类的父类,这是 Java 继承区别于 C++ 继承的一个特性
- 提高了类之间的耦合性(继承的缺点,耦合度高就会造成代码之间的联系越紧密,代码独立性越差)
继承关键字
- extends
- 在 Java 中,类的继承是单一继承,也就是说,一个子类只能拥有一个父类,所以 extends 只能继承一个类
public class base{ } public class child extends base{ }
- implements
- 使用 implements 关键字可以变相的使java具有多继承的特性,使用范围为类继承接口的情况,可以同时继承多个接口(接口跟接口之间采用逗号分隔)
public interface A { } public interface B { } public class C implements A,B { }
- super和this
- 通过super关键字来实现对父类成员的访问,用来引用当前对象的父类
- this指向自己的引用
public class Animal{ public void eat(){ } } public class Dog extends Animal{ public void eat(){ } public void test(){ this.eat(); super.eat(); } }
- final
- final 关键字声明类可以把类定义为不能继承的,即最终类;或者用于修饰方法,该方法不能被子类重写
- 声明类,不能被继承
final class ClassName{ }
- 声明方法,不能被子类重写
修饰符(public/private/default/protected) final 返回类型 方法名(){// method}
构造器
- 子类是不继承父类的构造器(构造方法或者构造函数)的,它只是调用(隐式或显式)。如果父类的构造器带有参数,则必须在子类的构造器中显式地通过
super
关键字调用父类的构造器并配以适当的参数列表 - 如果父类构造器没有参数,则在子类的构造器中不需要使用
super
关键字调用父类构造器,系统会自动调用父类的无参构造器class SuperClass { private int n; SuperClass(){ System.out.println("SuperClass()"); } SuperClass(int n) { System.out.println("SuperClass(int n)"); this.n = n; } } // SubClass 类继承 class SubClass extends SuperClass{ private int n; SubClass(){ // 自动调用父类的无参数构造器 System.out.println("SubClass"); } public SubClass(int n){ super(300); // 调用父类中带有参数的构造器 System.out.println("SubClass(int n):"+n); this.n = n; } }
继承类型
- Java支持多重继承,但不支持多继承
- 可以通过接口类(interface)和
implements
关键字变相使java具有多继承的特性
Java多态
- 多态的优点
- 消除类型之间的耦合关系
- 可替换性
- 可扩充性
- 接口性
- 灵活性
- 简化性
- 多态存在的三个必要条件
- 继承
- 重写
- 父类引用指向子类对象:
Parent p = new Child();
- 当使用多态方式调用方法时,首先检查父类中是否有该方法,如果没有,则编译错误;如果有,再去调用子类的同名方法
- 多态的好处:可以使程序有良好的扩展,并可以对所有类的对象进行通用处理
- 虚函数:虚函数的存在是为了多态。
- Java 中其实没有虚函数的概念,它的普通函数就相当于 C++ 的虚函数,动态绑定是Java的默认行为。如果 Java 中不希望某个函数具有虚函数特性,可以加上 final 关键字变成非虚函数
Java抽象类(与C++一致)
- 在面向对象的概念中,所有的对象都是通过类来描绘的,但是反过来,并不是所有的类都是用来描绘对象的,如果一个类中没有包含足够的信息来描绘一个具体的对象,这样的类就是抽象类
- 抽象类除了不能实例化对象之外,类的其它功能依然存在,成员变量、成员方法和构造方法的访问方式和普通类一样
- 由于抽象类不能实例化对象,所以抽象类必须被继承,才能被使用。也是因为这个原因,通常在设计阶段决定要不要设计抽象类
- 父类包含了子类集合的常见的方法,但是由于父类本身是抽象的,所以不能使用这些方法
- 在Java中抽象类表示的是一种继承关系,一个类只能继承一个抽象类,而一个类却可以实现多个接口
定义、继承抽象类
public abstract class Base{ // 定义抽象类
}
public class Child extends Base{ // 继承抽象类
}
抽象方法
- 如果你想设计这样一个类,该类包含一个特别的成员方法,该方法的具体实现由它的子类确定,那么你可以在父类中声明该方法为抽象方法
- Abstract 关键字同样可以用来声明抽象方法,抽象方法只包含一个方法名,而没有方法体
- 抽象方法没有定义,方法名后面直接跟一个分号,而不是花括号
public class ClassName{
public abstract void method();
// other
}
- 声明抽象方法会造成以下两个结果:
- 如果一个类包含抽象方法,那么该类必须是抽象类
- 任何子类必须重写父类的抽象方法,或者声明自身为抽象类
- 继承抽象方法的子类必须重写该方法。否则,该子类也必须声明为抽象类。最终,必须有子类实现该抽象方法,否则,从最初的父类到最终的子类都不能用来实例化对象
抽象类总结
-
抽象类不能被实例化(初学者很容易犯的错),如果被实例化,就会报错,编译无法通过。只有抽象类的非抽象子类可以创建对象
-
抽象类中不一定包含抽象方法,但是有抽象方法的类必定是抽象类
-
抽象类中的抽象方法只是声明,不包含方法体,就是不给出方法的具体实现也就是方法的具体功能
-
构造方法,类方法(用 static 修饰的方法)不能声明为抽象方法
-
抽象类的子类必须给出抽象类中的抽象方法的具体实现,除非该子类也是抽象类
Java接口
- 接口(英文:Interface),在JAVA编程语言中是一个抽象类型,是抽象方法的集合,接口通常以interface来声明。一个类通过继承接口的方式,从而来继承接口的抽象方法
- 接口并不是类,编写接口的方式和类很相似,但是它们属于不同的概念。类描述对象的属性和方法。接口则包含类要实现的方法
- 除非实现接口的类是抽象类,否则该类要定义接口中的所有方法
- 接口无法被实例化,但是可以被实现。一个实现接口的类,必须实现接口内所描述的所有方法,否则就必须声明为抽象类。另外,在 Java 中,接口类型可用来声明一个变量,他们可以成为一个空指针,或是被绑定在一个以此接口实现的对象
- 接口与类相似点:
- 一个接口可以有多个方法
- 接口文件保存在 .java 结尾的文件中,文件名使用接口名
- 接口的字节码文件保存在 .class 结尾的文件中
- 接口相应的字节码文件必须在与包名称相匹配的目录结构中
- 接口与类的区别:
- 接口不能用于实例化对象
- 接口没有构造方法
- 接口中所有的方法必须是抽象方法
- 接口不能包含成员变量,除了 static 和 final 变量
- 接口不是被类继承了,而是要被类实现
- 接口支持多继承
- 接口特性
- 接口中每一个方法也是隐式抽象的,接口中的方法会被隐式的指定为 public abstract(只能是 public abstract,其他修饰符都会报错)
- 接口中可以含有变量,但是接口中的变量会被隐式的指定为 public static final 变量(并且只能是 public,用 private 修饰会报编译错误)
- 接口中的方法是不能在接口中实现的,只能由实现接口的类来实现接口中的方法
- 抽象类和接口的区别
- 抽象类中的方法可以有方法体,就是能实现方法的具体功能,但是接口中的方法不行
- 抽象类中的成员变量可以是各种类型的,而接口中的成员变量只能是 public static final 类型的
- 接口中不能含有静态代码块以及静态方法(用 static 修饰的方法),而抽象类是可以有静态代码块和静态方法
- 一个类只能继承一个抽象类,而一个类却可以实现多个接口(实现多继承)
接口声明
// 声明模板
[可见度] interface 接口名称 [extends 其他的接口名] {
// 声明static、final字段
// 抽象方法
}
/* 文件名 : Animal.java */
interface Animal {
public void eat(); // 隐式抽象方法,不用abstract
public void travel();
}
重写接口声明方法的规则
- 重写接口中声明的方法时,需要注意以下规则:
- 类在实现接口的方法时,不能抛出强制性异常,只能在接口中,或者继承接口的抽象类中抛出该强制性异常
- 类在重写方法时要保持一致的方法名,并且应该保持相同或者相兼容的返回值类型
- 如果实现接口的类是抽象类,那么就没必要实现该接口的方法
- 在实现接口的时候,也要注意一些规则:
- 一个类可以同时实现多个接口
- 一个类只能继承一个类,但是能实现多个接口
- 一个接口能继承另一个接口,这和类之间的继承比较相似
接口(多)继承
- 接口继承
// 文件名: Sports.java
public interface Sports
{
public void setHomeTeam(String name);
public void setVisitingTeam(String name);
}
// 文件名: Football.java
public interface Football extends Sports
{
public void homeTeamScored(int points);
public void visitingTeamScored(int points);
public void endOfQuarter(int quarter);
}
// 文件名: Hockey.java
public interface Hockey extends Sports
{
public void homeGoalScored();
public void visitingGoalScored();
public void endOfPeriod(int period);
public void overtimePeriod(int ot);
}
/*
Hockey接口自己声明了四个方法,从Sports接口继承了两个方法,这样,实现Hockey接口的类需要实现六个方法。
相似的,实现Football接口的类需要实现五个方法,其中两个来自于Sports接口
*/
- 多继承
public interface Hockey extends Sports, Event