7 数组
7.1 数组概述
数组是相同类型数据的有序集合
可以通过下标来访问,下标从0开始
7.2 数组创建
声明创建
首先必须声明数组变量,才能在程序中使用数组。下面是声明数组变量的语法:
dataType[] arrayRefVar; // 首选的方法
dataType arrayRefVar[]; // 效果相同,但不是首选方法
Java语言使用new操作符来创建数组,语法如下:
arrayRefVar = new dataType[arraySize];
上面的语法语句做了两件事:
- 使用 dataType[arraySize] 创建了一个数组。
- 把新创建的数组的引用赋值给变量 arrayRefVar。
当然也可以使用一行代码完成声明和创建:
int[] numbers = new int[100];
可以通过numbers.length获取长度
初始化
有三种初始化
-
静态初始化:
int[] numbers = {1,2,3}; Person[] people = {new Person(),new Person()};
-
动态初始化:包含默认初始化
int[] numbers = new int[10]; b[0] = 1;
-
默认初始化:数组是引用类型,其中的元素相当于类的实例变量,因此数组一经分配空间,其中的元素就按照实例变量同样的方式被隐式初始化。
内存分析
Java内存分为:
- 堆:存放new的对象和数组
- 栈:存放基本变量类型,包括具体数值。存放引用对象的变量,包括具体地址。
- 方法区:包含所有class和static变量,方法区是特殊的堆。
声明数组时,数组并不存在,只是在栈中压入一个变量
创建数组时,数组才会存在,在堆中真正地开辟出空间
下标越界
访问下标超出数组大小会产生下标越界异常。
基本特点
- 长度确定
- 相同类型
- 元素可以是任意的类型
- 数组是引用类型,数组可以看成是对象,Java中的对象保存在堆中,无论数组对象中存什么数据类型,数组所占空间是在堆中的,返回的引用是在栈里的。引用:栈--->堆
7.3 数组使用
- 使用for循环
- 使用for-each循环即增强型for
- 使用数组作为方法的参数
- 使用数组作为返回值
7.4 多维数组
数组的元素还是数组
int[][] nums = new int[2][5];
int[][] numbers = {{1,2},{3,4},{5,6}};
7.5 Arrays类
数组的工具类java.util.Arrays
Arrays类中的方法都是static修饰的静态方法,可以直接使用类名调用
有以下常用功能
- 给数组赋值:通过 fill 方法。
- 对数组排序:通过 sort 方法,按升序。
- 比较数组:通过 equals 方法比较数组中元素值是否相等。
- 查找数组元素:通过 binarySearch 方法能对排序好的数组进行二分查找法操作。
7.6 冒泡排序
public static void sort(int[] array){
for(int i=0;i<array.length-1;i++){
boolean flag = false;//优化,跳过没有意义的比较轮次
//减i是因为进行i次比较后,产生了i个最大或最小的数不需要进行下次比较
for(int j=0;j<array.length-1-i;j++){
if(array[j+1]<array[j])
swap(array[j+1],array[j]);
}
if(flag == flase)
break;
}
}
7.7 稀疏数组
数组中大部分为0,或其他相同元素时,采用的一种压缩存储方式。
即只记录数组行列等基本信息,以及有效信息
8 面向对象
8.1 初识面向对象
面向过程
面向对象
以类的方式组织代码,以对象的方式封装数据
8.2 方法
回顾:如何定义方法
静态方法:可以通过类名调用
非静态方法:需要实例化对象,借用对象调用
形参实参
值传递引用传递
this关键字:用来表示当前对象本身,或当前类的一个实例,通过 this 可以调用本对象的所有方法和属性。
8.3 对象创建分析
使用new创建对象,创建时会
- 分配内存空间
- 默认初始化
- 调用构造方法
构造方法
- 方法名和类名相同
- 没有返回类型,不能写成void
一旦定义了有参构造,无参构造就必须显式定义
new本质是在调用构造器
内存分析
8.4 三大特性
封装
高内聚,低耦合
- 模块内部联系紧密
- 模块之间联系松散
封装:数据的隐藏,将数据封装起来,只暴露给外界接口
属性:private
方法:getter/setter
封装的优点:
- 良好的封装能够减少耦合。
- 类内部的结构可以自由修改。
- 可以对成员变量进行更精确的控制。
- 隐藏信息,实现细节。
继承
继承是类于类之间的关系,除此之外类与类之间的关系还有依赖,组合,聚合。继承:is a关系
Java只有单继承
控制访问与继承的规则:
- 父类中声明为 public 的方法在子类中也必须为 public。
- 父类中声明为 protected 的方法在子类中要么声明为 protected,要么声明为 public,不能声明为 private。
- 父类中声明为 private 的方法,不能够被继承。
Object类
在Java中,所有的类都继承了Object类
super关键字:表示当前类的父类
- super调用父类的构造方法,必须在父类方法的第一个
- super只能出现在子类方法或者是构造方法中
- super和this不能同时调用构造方法
一般在写了带参数构造器后,都需要显式写出无参构造器
方法重写
重写是方法的重写与属性无关
静态方法:方法调用只和定义时的类有关,其实是通过类名在调用
非静态方法:重写,用实例化对象调用
父类
public class A {
public static void test_static(){
System.out.println("A=>test_static");
}
public void test(){
System.out.println("A=>test");
}
}
子类
public class B extends A{
public static void test_static(){
System.out.println("B=>test_static");
}
@Override
public void test(){
System.out.println("B=>test");
}
}
测试
public class test {
public static void main(String[] args) {
B b = new B();
A a= new B();
b.test_static();
a.test_static();
a.test();
b.test();
}
}
输出
B=>test_static
A=>test_static
B=>test
B=>test
总结
- 重写需要有继承关系,子类重写父类的关系
- 方法名相同
- 参数列表相同
- 修饰符:范围可以扩大但不能缩小
- 父类是protected子类改为public
- 异常:可以缩小但不能扩大
- 父类是Exception子类是ClassNotFoundException
不能重写的方法
- static方法属于类,不属于实例
- final修饰的方法
- private 私有的方法无法重写
不能继承的类
- 被final修饰的类
多态
动态编译:类型在执行时才能确定
同一方法可以根据发送对象的不同而采用多种不同的行为方式
一个对象的实际类型是确定的
但可以指向的引用类型就不一定了,父类引用可以指向子类的对象
父类
public class Person {
public void run(){
System.out.println("run");
}
}
子类
public class Student extends Person{
public void eat(){
System.out.println("eat");
}
@Override
public void run() {
System.out.println("sonRun");;
}
}
测试
public class Test {
public static void main(String[] args) {
Student s1 = new Student();
Person s2 = new Student();
s1.run();//两个类型都有run方法且子类重写,执行子类的
s2.run();
s1.eat();//eat方法是子类独有的,父类无法调用
//s2.eat();//报错
}
}
输出
sonRun
sonRun
eat
总结:
多态是方法的多态,属性没有多态
多态存在条件:
- 有继承关系
- 子类重写父类的方法
- 父类引用指向子类对象
多态的实现可以有以下几种:
- 重写
- 接口
- 抽象类和抽象方法
8.5 instanceof
判断一个类型是不是某种类型
A instanceof B
看A和B有没有继承关系,
同级别类是无法通过编译的
8.6 类型转换
引用类型转换
同样的高转低要强制转换
低转高是直接实现可以的
如果子类转为父类就可能会丢失一些子类自己本来的方法
子类转父类 向上转型
父类转子类 向下转型 强制转换
8.7 static
static变量
static变量也称作静态变量,静态变量和非静态变量的区别是:静态变量被所有的对象所共享,在内存中只有一个副本【存放在方法区】,它当且仅当在类初次加载时会被初始化【加final和不加final的static变量初始化的位置不一样】。而非静态变量是对象所拥有的,在创建对象的时候被初始化,存在多个副本,各个对象拥有的副本互不影响。
static方法
static方法一般称作静态方法,由于静态方法不依赖于任何对象就可以进行访问,因此对于静态方法来说,是没有this的,因为它不依附于任何对象,既然都没有对象,就谈不上this了。并且由于这个特性,在静态方法中不能访问类的非静态成员变量和非静态成员方法,因为非静态成员方法/变量都是必须依赖具体的对象才能够被调用。
static静态代码块
static关键字还有一个比较关键的作用就是 用来形成静态代码块以优化程序性能。static块可以置于类中的任何地方,类中可以有多个static块。在类初次被加载的时候,会按照static块的顺序来执行每个static块,并且只会执行一次【根据class加载原理 每个类加载一次 使用双亲委托加载】。
初始化的顺序 静态代码块 > 构造代码块 > 构造函数
测试
public class Test {
{
System.out.println("匿名代码块");
}
static{
System.out.println("静态代码块");
}
public Test(){
System.out.println("构造方法");
}
public static void main(String[] args) {
Test test = new Test();
System.out.println("=========");
Test test2 = new Test();
}
}
执行
静态代码块
匿名代码块
构造方法
=========
匿名代码块
构造方法
static块可以用来优化程序性能,是因为它的特性:只会在类加载的时候执行一次,之后会按匿名代码块,构造方法的顺序执行
很多时候会将一些只需要进行一次的初始化操作都放在static代码块中进行。
静态内部类
pass
静态导包
import static java.lang.Math.random;//下面使用时可以直接调用random()
8.8 抽象类
abstract修饰符可以修饰方法也可以修饰类,abstract修饰方法就是抽象方法,abstract修饰类就是抽象类。
抽象类中可以没有抽象方法,但是一旦定义的抽象方法就必须声明为抽象类。抽象类也可以写普通方法。
抽象类:不能实例化,不能new,它就是用来让别人继承的
抽象方法:只有方法的声明,没有实现,子类需要实现。
子类继承抽象类就需要实现抽象方法,否则它也应当声明为抽象类
但是抽象类本质还是一个类,别人需要用extends继承,但是Java只有单继承
这是需要引入接口,接口可以实现多继承
思考:抽象类也存在构造器
8.9 接口
接口:只有规范。可以实现约束和实现分离--->面向接口编程
接口就是规范,定义的是一组规则。体现的是现实世界中“如果你是...则必须能...”的思想
接口的本质是契约,像法律,制定好了大家都要遵守
面向对象的精髓,是对对象的抽象,最能体现这一点的就是接口。
设计模式其实就是在研究:如何合理地去抽象
接口中定义方法都是抽象的,而且默认是public abstract,可以不写
接口中定义的属性都是常量,默认public static final,一般没人在接口里定义属性
使用接口时要注意锻炼自己的抽象思维能力
总结
- 接口是约束
- 接口定义一些方法,让不同的人实现
- 接口方法默认是public abstract
- 接口属性默认是public static final
- 接口不能被实例化,接口也没有构造方法
- implements可以实现多个接口
8.10 内部类
内部类就是在类的内部再定义一个类
一个java类可以有多个class,但只能有一个public class
-
成员内部类
-
静态内部类
-
局部内部类
-
匿名内部类
9 异常
什么是异常
程序运行时出现的不期而至的情况,文件找不到,网络链接失败等
要理解Java异常处理是如何工作的,你需要掌握以下三种类型的异常:
- 检查性异常:最具代表的检查性异常是用户错误或问题引起的异常,这是程序员无法预见的。例如要打开一个不存在文件时,一个异常就发生了,这些异常在编译时不能被简单地忽略。
- 运行时异常: 运行时异常是可能被程序员避免的异常。与检查性异常相反,运行时异常可以在编译时被忽略。
- 错误: 错误不是异常,而是脱离程序员控制的问题。错误在代码中通常被忽略。例如,当栈溢出时,一个错误就发生了,它们在编译也检查不到的。
异常体系结构
所有的异常类是从 java.lang.Exception 类继承的子类。
Exception 类是 Throwable 类的子类。除了Exception类外,Throwable还有一个子类Error 。
Java 程序通常不捕获错误。错误一般发生在严重故障时,它们在Java程序处理的范畴之外。
Error 用来指示运行时环境发生的错误。
例如,JVM 内存溢出。一般地,程序不会从错误中恢复。
异常类有两个主要的子类:IOException 类和 RuntimeException 类。
Error
Error类对象由Java虚拟机生成并抛出,大多数错误与代码编写者执行的操作无关。
Error是灾难性的致命的错误,是程序无法控制和处理的
Exception
Exception通常是可以被程序处理的。我们应当尽量避免一些异常的产生。
捕获和抛出异常
抛出异常
捕获异常
五个关键字
try,catch,finally,throw,throws
主动抛出异常
一般在方法中使用。
if(b == 0){
throw new ArithmeticException();
}
在方法上抛出异常throws
public void method(int a,int b) throws IOException{
}
自定义异常
在 Java 中你可以自定义异常。编写自己的异常类时需要记住下面的几点。
- 所有异常都必须是 Throwable 的子类。
- 如果希望写一个检查性异常类,则需要继承 Exception 类。
- 如果你想写一个运行时异常类,那么需要继承 RuntimeException 类。
可以像下面这样定义自己的异常类:
class MyException extends Exception{
}
实例
public class MyException extends Exception
{
private int number;
public MyException(int number)
{
this.number = number;
}
@Override
public String toString(){
return "MyException: "+number;
}
}