Java学习(二)接一
9. 数组
- 数组是相同类型数据的有序集合。
- 数组描述的是相同类型的若干个数据,按照一定的先后次序排列组合而成。
- 其中,每一个数据称作一个数组元素,每个数组元素可以通过一个下标来访问它们。
9.1 声明数组变量
dataType[] arrayRefVar; // 首选的方法
或
dataType arrayRefVar[]; // 效果相同,但不是首选方法
例
int[] n1;
int n1[];
9.2 创建数组
Java语言使用new操作符来创建数组,语法如下(动态初始化,先分配空间):
arrayRefVar = new dataType[arraySize];
数组变量的声明,和创建数组可以用一条语句完成,如下所示(动态初始化,先分配空间):
dataType[] arrayRefVar = new dataType[arraySize];
还可以使用如下的方式创建数组(静态初始化,在初始化数组时为数组每个元素赋值):
dataType[] arrayRefVar = {value0, value1, ..., valuek};
数组的元素是通过索引访问的。数组索引从 0 开始,所以索引值从 0 到 arrayRefVar.length-1。
public class demo {
public static void main(String[] args) {
double[] myList; //声明数组
//创建数组
myList= new double[10];
myList[0] = 5.6;
myList[1] = 4.5;
myList[2] = 3.3;
myList[3] = 13.2;
myList[4] = 4.0;
myList[5] = 34.33;
myList[6] = 34.0;
myList[7] = 45.45;
myList[8] = 99.993;
myList[9] = 11123;
// 计算所有元素的总和
double total = 0;
for (int i = 0; i < 10; i++) {
total += myList[i];
}
System.out.println("总和为: " + total);
}
}
总和为: 11367.373

9.3 数组使用
9.3.1 For-Each 循环
之前循环中提到过
public class demo {
public static void main(String[] args) {
// 定义数组
double[] myList;
myList= new double[5];
myList[0] = 5.6;
myList[1] = 4.5;
myList[2] = 3.3;
myList[3] = 13.2;
myList[4] = 4.0;
for (double v : myList) {
System.out.println(v);
}
}
}
5.6
4.5
3.3
13.2
4.0
9.3.2 数组作为方法的参数
数组可以作为参数传递给方法。
public class demo {
public static void main(String[] args) {
// 定义数组
double[] myList;
myList= new double[5];
myList[0] = 5.6;
myList[1] = 4.5;
myList[2] = 3.3;
myList[3] = 13.2;
myList[4] = 4.0;
printArray(myList);
}
public static void printArray(double[] array) {
for (double v : array) {
System.out.println(v);
}
}
}
5.6
4.5
3.3
13.2
4.0
9.3.3 数组作为方法的返回值
定义revers方法返回一个反转后的数组。
public class demo {
public static void main(String[] args) {
// 定义数组
double[] myList;
myList= new double[5];
myList[0] = 5.6;
myList[1] = 4.5;
myList[2] = 3.3;
myList[3] = 13.2;
myList[4] = 4.0;
double[] myList2= reverse(myList);
for (double v : myList2) {
System.out.println(v);
}
}
public static double[] reverse(double[] list) {
double[] result = new double[list.length];
for (int i = 0, j = result.length - 1; i < list.length; i++, j--) {
result[j] = list[i];
}
return result;
}
}
4.0
13.2
3.3
4.5
5.6
9.4 多维数组
多维数组可以看成是数组的数组,比如二维数组就是一个特殊的一维数组,其每一个元素都是一个一维数组,例:
public class demo {
public static void main(String[] args) {
int[][] num = new int[2][3];
num[1][2]=1; //为num[1][2]赋值,其余为默认值
for (int[] ints : num) {
for (int anInt : ints) {
System.out.println(anInt);
}
}
}
}
0
0
0
0
0
1
9.5 Arrays 类
java.util.Arrays 类能方便地操作数组,它提供的所有方法都是静态的。
具有以下功能:
- 给数组赋值:通过 fill 方法。
- 对数组排序:通过 sort 方法,按升序。
- 比较数组:通过 equals 方法比较数组中元素值是否相等。
- 查找数组元素:通过 binarySearch 方法能对排序好的数组进行二分查找法操作。
Arrays类中的方法都是static修饰的静态方法,在使用时可以直接使用类名进行调用。
import java.util.Arrays;
public class demo {
public static void main(String[] args) {
int[] a1= {5,8,4,2,9};
// Arrays.toString打印数组元素
System.out.println(Arrays.toString(a1));
int[] a2=new int[5];
Arrays.fill(a2,5); //a2的所有值都赋值为5
System.out.println(Arrays.toString(a2));
Arrays.sort(a1); //对a1升序排序
System.out.println(Arrays.toString(a1));
boolean f1=Arrays.equals(a1,a2); //a1,a2是否相等
System.out.println(f1);
int[] a3=new int[5];
Arrays.fill(a3,0,5,5); //a3的0跟5之间的元素填充为5(不包括第五个)
System.out.println(Arrays.toString(a3));
boolean f2=Arrays.equals(a2,a3); //a2,a3是否相等
System.out.println(f2);
int k=Arrays.binarySearch(a1,4); //二分查找查找4在a1中的位置
System.out.println(k);
}
}
[5, 8, 4, 2, 9]
[5, 5, 5, 5, 5]
[2, 4, 5, 8, 9]
false
[5, 5, 5, 5, 5]
true
1
10. 面向对象
面向对象的本质是:以类的方法组织代码,以对象的组织封装数据。
面向对象的三大特性是封装、继承和多态。
10.1 创建对象
对象是根据类创建的。在Java中,使用关键字 new 来创建一个新的对象。
public class demo{
public static void main(String[] args) {
//类实例化后会返回一个自己的对象
Student s = new Student(); //s对象是Student类的具体实例
s.name="abc";
s.age=20;
System.out.println(s.name+" "+s.age);
}
}
class Student {
String name;
int age;
}
每个类都有构造方法。如果没有显式地为类定义构造方法,Java 编译器将会为该类提供一个默认构造方法。
在创建一个对象的时候,至少要调用一个构造方法。构造方法的名称必须与类同名,一个类可以有多个构造方法。
上一个例子就是调用了默认的构造方法:
public Student(){}
多个构造方法,例
public class demo{
public static void main(String[] args) {
//类实例化后会返回一个自己的对象
Student s = new Student(); //s对象是Student类的具体实例
s.name="abc";
s.age=20;
System.out.println(s.name+" "+s.age);
Student s2 = new Student("def");//调用含一个参数的构造方法
System.out.println(s2.name);
}
}
class Student {
String name;
int age;
public Student(){
}
//定义有参构造方法会覆盖掉默认的无参构造方法,如需同时使用要手动写一个无参构造方法
public Student(String name){
this.name=name;
System.out.println("此方法被调用");
}
}
abc 20
此方法被调用
def
10.2 封装
程序设计应追求“高内聚,低耦合”。高内聚就是类的内部数据操作细节自己完成,不允许外部干涉;低耦合要仅暴露少量的方法给外部使用。
封装是指一种将抽象性函式接口的实现细节部分包装、隐藏起来的方法。
实现封装的步骤
1.修改属性的可见性来限制对属性的访问。
class Student {
private String name;
private int age;
}
这段代码中,将 name 和 age 属性设置为私有的,只能本类才能访问,其他类都访问不了,如此就对信息进行了隐藏。
2. 对每个值属性提供对外的公共方法访问,也就是创建一对赋取值方法,用于对私有属性的访问,例如:
class Student {
private String name;
private int age;
public String getName(){
return name;
}
public int getAge(){
return age;
}
public void setName(String name){
this.name = name;
}
public void setAge(int age){
this.age = age;
}
}
采用 this 关键字是为了解决实例变量和局部变量之间发生的同名的冲突。
10.3 继承
继承就是子类继承父类的特征和行为,使得子类对象(实例)具有父类的实例域和方法,或子类从父类继承方法,使得子类具有父类相同的行为。
10.3.1 extends关键字
在 Java 中通过 extends 关键字可以申明一个类是从另外一个类继承而来的,一般形式如下:
class 父类 {
}
class 子类 extends 父类 {
}
例:
//父类
class Person{
public String name;
private int age;
public void say(){
System.out.println("speak");
}
}
//子类
class Student extends Person {
}
public class demo{
public static void main(String[] args) {
Student s1 = new Student();
System.out.println(s1.name);//通过子类访问父类属性
// System.out.println(s1.age); 私有的属性和方法无法继承
s1.say();//通过子类访问父类方法
}
}
null
speak
需要注意的是 Java 不支持多继承,但支持多重继承。

10.3.2 super、this关键字
-
super关键字:我们可以通过super关键字来实现对父类成员的访问,用来引用当前对象的父类。
-
this关键字:指向自己的引用。
关于构造方法:
-
子类是不继承父类的构造器(构造方法或者构造函数)的,它只是调用(隐式或显式)。如果父类的构造器带有参数,则必须在子类的构造器中显式地通过 super 关键字调用父类的构造器并配以适当的参数列表。
-
如果父类构造器没有参数,则在子类的构造器中不需要使用 super 关键字调用父类构造器,系统会自动调用父类的无参构造器。
例
//父类
class Person{
public String name = "abc";
public int age = 20;
public Person(){
System.out.println("Person的构造方法执行");
}
public void say(){
System.out.println("speak");
}
}
//子类
class Student extends Person {
private int age = 200;
public Student(){
// 默认调用了父类的无参构造
// super();//调用父类的构造方法必须在子类构造方法第一行
System.out.println("Student的构造方法执行");
}
public void test(int age){
System.out.println(age);
// 输出传入的值
System.out.println(this.age);
// 输出Student的属性
System.out.println(super.age);
// 输出父类的属性
}
}
public class demo{
public static void main(String[] args) {
Student s1 = new Student();
s1.test(500);
}
}
Person的构造方法执行
Student的构造方法执行
500
200
20
10.4 重写
重写是子类对父类的允许访问的方法的实现过程进行重新编写,
- 方法名必须相同
- 参数列表必须相同
- 修饰符范围可以扩大不能缩小(publi>protected>default>private)
重写子类和父类方法必须一致,方法体不同。
//父类
class Person{
public void say(){
System.out.println("person");
}
}
//子类
class Student extends Person {
public void say(){// 子类对父类的方法重写
System.out.println("student");
}
}
public class demo{
public static void main(String[] args) {
Student s = new Student();
s.say();
Person p = new Person();
p.say();
}
}
student
person
10.5 多态
- 多态是方法的多态,属性没有多态
- 在父类和子类中
- 有继承关系,方法重写,父类引用指向子类对象(Father f = new Son())
例
//父类
class Person{
public void say(){
System.out.println("person");
}
}
//子类
class Student extends Person {
public void say(){// 子类对父类的方法重写
System.out.println("student");
}
}
public class demo{
public static void main(String[] args) {
Person s = new Student();//Student对象
s.say();//执行Student类的方法
Person p = new Person();//Person对象
p.say();//执行Person类的方法
}
}
student
person
尽管s是Person类型,但仍然运行Student类的方法。因为在编译阶段,只是检查参数的引用类型,然后在运行时,Java 虚拟机指定对象的类型并且运行该对象的方法。
当使用多态方式调用方法时,首先检查父类中是否有该方法,如果没有,则编译错误;如果有,再去调用子类的同名方法。
11. 关于static关键字
11.1 静态变量、静态方法
class Student {
static int age;//静态变量
double score;//非静态变量
public void run(){
}
public static void go(){
}
}
public class demo{
public static void main(String[] args) {
Student s = new Student();
System.out.println(s.score);
// 静态变量可以使用类名.属性访问
System.out.println(Student.age);
// 方法也是一样
s.run();
Student.go();
}
}
11.2, 静态代码块
class Student {
{
System.out.println("匿名代码块");
}
static {
System.out.println("静态代码块");
}
public Student() {
System.out.println("构造方法");
}
}
public class demo{
public static void main(String[] args) {
Student s = new Student();
System.out.println("a");
Student s2 = new Student();
}
}
静态代码块
匿名代码块
构造方法
a
匿名代码块
构造方法
在实例化时先执行静态代码块再执行匿名代码块最后执行构造方法。且静态代码块只会执行一次。
11.3. 静态导入包
以Math.random()为例:
正常使用方法
System.out.println(Math.random());
静态导入后可直接使用random()
import static java.lang.Math.random;
System.out.println(random());
12. 抽象类
抽象类不能用来实例化对象,声明抽象类的唯一目的是为了将来对该类进行扩充。
abstract class Action{
// 抽象方法,只有方法名字,没有具体的实现
public abstract void doSomething();
public void say() {
}
}
class Student extends Action{
// 子类必须重写抽象方法
public void doSomething() {
}
}
抽象类中可以写普通的方法,抽象方法必须写在抽象类中。
13. 接口
- 接口是一个抽象类型,是抽象方法的集合,接口通常以interface来声明。一个类通过继承接口的方式,从而来继承接口的抽象方法。
- 除非实现接口的类是抽象类,否则该类要定义接口中的所有方法。
- 接口无法被实例化,但是可以被实现。
对比
- 普通类:只要具体实现;
- 抽象类:具体实现和规范(抽象方法)都有;
- 接口:只有规范,自己无法写方法。约束和实现分离。
例
//interface关键字定义接口,接口需要有实现类
interface Service{
// public abstract void add();
// 接口中的方法默认是public abstract,可以省略不写
void add(String name);
void del(String name);
}
//实现接口的类必须重写类中的所有方法
class ServiceImpl implements Service{
public void add(String name) {
}
public void del(String name) {
}
}
}
接口可以多继承
interface Service{
void add(String name);
void del(String name);
}
interface Service2{
void add2(String name);
void del2(String name);
}
//实现接口的类必须重写类中的所有方法
class ServiceImpl implements Service,Service2{
public void add(String name) {
}
public void del(String name) {
}
public void add2(String name) {
}
public void del2(String name) {
}
}
14. 内部类
内部类就是在一个类的内部再定义一个类,比如,A类中定义一个B类,那么B类相对A类来说就称为内部类,而A类相对B类来说就是外部类。
14.1. 成员内部类
Outer.java
public class Outer {
private int id=20;
public void out(){
System.out.println("这是外部类的方法");
}
class Inner{
public void in(){
System.out.println("这是内部类的方法");
}
// 内部类可以获得外部类的私有属性
public void getID(){
System.out.println(id);
}
}
}
Application.java
public class Application {
public static void main(String[] args) {
Outer o = new Outer();
// 通过外部类来实现内部类
Outer.Inner i = o.new Inner();
o.out();
i.getID();
i.in();
}
}
14.2. 静态内部类
Outer.java
public class Outer {
private int id=20;
public void out(){
System.out.println("这是外部类的方法");
}
// 静态内部类不能直接访问外部类的非静态属性
public static class Inner{
public void in(){
System.out.println("这是内部类的方法");
}
// 内部类可以获得外部类的私有属性
// public void getID(){
// System.out.println(id);
// }
}
}
14.3. 局部内部类
public class Outer {
public void method(){
// 局部内部类
class Inner{
}
}
}
15. 异常处理
15.1 基本类型
- Java把异常当作对象来处理,并定义一个基类 java.lang.Throwable作为所有异常的超类。
- 在Java api中定义了许多异常类,这些异常类分为两大类,错误Error和异常Exception。

三种类型的异常:
-
检查性异常
最具代表的检查性异常是用户错误或问题引起的异常,这是程序员无法预见的。例如要打开一个不存在文件时,一个异常就发生了,这些异常在编译时不能被简单地忽略。 -
运行时异常
运行时异常是可能被程序员避免的异常。与检查性异常相反,运行时异常可以在编译时被忽略。 -
错误ERROR
错误不是异常,而是脱离程序员控制的问题。错误在代码中通常被忽略。例如,当栈溢出时,一个错误就发生了,它们在编译也检查不到的。
15.2 捕获和抛出异常
使用 try 和 catch 关键字可以捕获异常。
public class demo01 {
public static void main(String[] args) {
int a=1;
int b=0;
try {//try监控区域
System.out.println(a/b);
}catch (Exception e){
System.out.println("出现异常");
}finally {//最后一定执行
System.out.println("finally");
}
}
}
出现异常
finally
throws 用在方法上,声明该方法的功能可能会出现异常。可以抛给虚拟机处理,或者使用 try....catch... 进行处理。
public class demo01 {
public static void main(String[] args) {
int a=1;
int b=0;
try {
new demo01().test(1,0);
} catch (Exception e) {
System.out.println(e.toString());
}
}
public void test(int a,int b) throws ArithmeticException{
if(b==0){
throw new ArithmeticException();
}
}
}
java.lang.ArithmeticException
15.3 自定义异常
在 Java 中你可以自定义异常。编写自己的异常类时需要记住下面的几点。
- 所有异常都必须是 Throwable 的子类。
- 如果希望写一个检查性异常类,则需要继承 Exception 类。
- 如果你想写一个运行时异常类,那么需要继承 RuntimeException 类。
例
public class demo01 {
public static void main(String[] args) {
try {
new demo01().test(20);
} catch (myException e) {
System.out.println(e.toString());
}
}
public void test(int a) throws myException{
if(a>10){
throw new myException(a);
}
}
public static class myException extends Exception{
// 数字大于10抛出异常
int num;
public myException(int n){
this.num=n;
}
// 用于打印异常信息
public String toString() {
return "myException{" +
"num=" + num +
'}';
}
}
}
myException
16. 流Stream、文件File和IO
一个流被定义为一个数据序列。输入流用于从源读取数据,输出流用于向目标写数据。
下图是一个描述输入流和输出流的类层次图。

16.1. FileInputStream、FileOutputStream
FileInputStream类用于从文件读取数据。
可以使用字符串类型的文件名来创建一个输入流对象来读取文件:
InputStream f = new FileInputStream("C:/java/hello");
也可以使用一个文件对象来创建一个输入流对象来读取文件。我们首先得使用 File() 方法来创建一个文件对象:
File f = new File("D:/ellll/hello");
InputStream in = new FileInputStream(f);
FileOutputStream类用来创建一个文件并向文件中写数据。
如果该流在打开文件进行输出前,目标文件不存在,那么该流会创建该文件。
使用字符串类型的文件名来创建一个输出流对象:
OutputStream f = new FileOutputStream("C:/java/hello")
也可以使用一个文件对象来创建一个输出流来写文件。我们首先得使用File()方法来创建一个文件对象:
File f = new File("D:/ellll/hello");
OutputStream fOut = new FileOutputStream(f);
使用实例
import java.io.*;
import java.nio.charset.StandardCharsets;
public class testFile {
public static void main(String[] args) throws IOException{
File f = new File("test.txt");
FileOutputStream os = new FileOutputStream(f);
//创建FileOutputStream对象,文件不存在会自动创建
OutputStreamWriter writer = new OutputStreamWriter(os, StandardCharsets.UTF_8);
//使用指定的字符集将写入其中的字符编码为字节,这里编码方式是utf-8
writer.append("文本内容");
//写入缓冲区
writer.close();
//关闭写入流,同时会把缓冲区内容写入文件
os.close();
//关闭输出流,释放系统资源
FileInputStream is = new FileInputStream(f);
//创建FileInputStream对象
InputStreamReader reader = new InputStreamReader(is,StandardCharsets.UTF_8);
//创建InputStreamReader对象,编码与写入相同
StringBuffer sb = new StringBuffer();
while (reader.ready()) {
sb.append((char) reader.read());
// 转成char加到StringBuffer对象中
}
System.out.println(sb.toString());
reader.close();
//关闭读取流
is.close();
//关闭输入流,释放系统资源
}
}
16.2. 文件File、IO
16.2.1. 创建目录
File类中有两个方法可以用来创建文件夹:
- mkdir( )方法创建一个文件夹,成功则返回true,失败则返回false。失败表明File对象指定的路径已经存在,或者由于整个路径还不存在,该文件夹不能被创建。
- mkdirs()方法创建一个文件夹和它的所有父文件夹。
下面的例子创建 "/tmp/user/java/bin"文件夹:
import java.io.File;
public class CreateDir {
public static void main(String[] args) {
String dirname = "/tmp/user/java/bin";
File d = new File(dirname);
// 现在创建目录
d.mkdirs();
}
}
16.2.2. 读取目录
一个目录其实就是一个 File 对象,它包含其他文件和文件夹。
如果创建一个 File 对象并且它是一个目录,那么调用 isDirectory() 方法会返回 true。
可以通过调用该对象上的 list() 方法,来提取它包含的文件和文件夹的列表。
import java.io.File;
public class DirList {
public static void main(String args[]) {
String dirname = "/tmp";
File f1 = new File(dirname);
if (f1.isDirectory()) {
System.out.println("目录 " + dirname);
String s[] = f1.list();
for (int i = 0; i < s.length; i++) {
File f = new File(dirname + "/" + s[i]);
if (f.isDirectory()) {
System.out.println(s[i] + " 是一个目录");
} else {
System.out.println(s[i] + " 是一个文件");
}
}
} else {
System.out.println(dirname + " 不是一个目录");
}
}
}
目录 /tmp
bin 是一个目录
lib 是一个目录
demo 是一个目录
test.txt 是一个文件
README 是一个文件
index.html 是一个文件
include 是一个目录
16.2.3. 删除目录或文件
删除文件可以使用 java.io.File.delete() 方法。
以下代码会删除目录 /tmp/java/,需要注意的是当删除某一目录时,必须保证该目录下没有其他文件才能正确删除,否则将删除失败。
import java.io.File;
public class DeleteFileDemo {
public static void main(String[] args) {
// 这里修改为自己的测试目录
File folder = new File("/tmp/java/");
deleteFolder(folder);
}
// 删除文件及目录
public static void deleteFolder(File folder) {
File[] files = folder.listFiles();
if (files != null) {
for (File f : files) {
if (f.isDirectory()) {
deleteFolder(f);
} else {
f.delete();
}
}
}
folder.delete();
}
}
17. 输入
17.1. BufferedReader
把 System.in 包装在一个 BufferedReader 对象中来创建一个字符流。BufferedReader 对象创建后,可以使用 read() 方法从控制台读取一个字符,或者用 readLine() 方法读取一个字符串。用read()方法会把字符串拆成单个字符,需要循环才能全部输出。
import java.io.*;
public class BRRead {
public static void main(String[] args) throws IOException {
String s;
// 使用 System.in 创建 BufferedReader
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
System.out.println("输入字符串");
// 读取字符串
s = br.readLine();
System.out.println(s);
}
}
输入后按回车会输出刚刚输入的字符串
17.2. Scanner 类
通过 Scanner 类来获取用户的输入
- 使用hasNext判断是否还有输入的数据
例
import java.util.Scanner;
public class testSc {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
//创建一个scanner类
//使用hasNext判断是否还有输入的数据
if(scanner.hasNext()){
String s1 = scanner.next();
System.out.println(s1);
}
scanner.close();
}
}
hello world
hello
空格之后的字符串并未输出
- 使用hasNextLine判断是否还有输入的数据
例
import java.util.Scanner;
public class testSc2 {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
//创建一个scanner类
//使用hasNextLine判断是否还有输入的数据
if(scanner.hasNextLine()){
String s1 = scanner.nextLine();
System.out.println(s1);
}
scanner.close();
}
}
hello world
hello world
空格之后的字符串输出了
next() 与 nextLine() 区别
next():
- 一定要读取到有效字符后才可以结束输入。
- 对输入有效字符之前遇到的空白,next() 方法会自动将其去掉。
- 只有输入有效字符后才将其后面输入的空白作为分隔符或者结束符。
next() 不能得到带有空格的字符串。
nextLine():
- 以Enter为结束符,也就是说 nextLine()方法返回的是输入回车之前的所有字符。
- 可以获得空白。

浙公网安备 33010602011771号