数组
数组类型
- 数组是编程语言中最常见的一种数据结构, 可用于存储多个数据,每个数组元素存放一个数据,通常可通过数组元素的索引来访问数组元素,包括为数组元素赋值和取出数组元素的值。
理解数组
- 因为Java语言是面向对象的语言,而类与类之间可以支持继承关系,这样可能产生一个数组里可以存放多种数据类型的假象。例如有一个水果数组,要求每个数组元素都是水果,实际上数组元素既可以是苹果,也可以是香蕉(苹果、香蕉都继承了水果,都是一种特殊的水果),但这个数组的数组元素的类型还是唯一的,只能是水果类型。
定义数组
Java语言支持两种语法格式来定义数组:
数据类型 [] 数组名
type [] arrayName;
type arrayName[] ;
数据类型:
告知编译器,当前数组中能够保存的数据类型到底是什么?并且在确定数据类
型之后,整个数组中保存的数据类型无法修改!!!
[]:
1. 告知编译器这里定义的是一个数组类型数据。
2. 明确告知编译器,数组名是一个【引用数据类型】
数组名:
1. 操作数据非常重要的数据!!!
2. 数组名是一个【引用数据类型】
小拓展: int(*) (void *, void *)
new 数据类型[容量];
new 数据类型[容量];
= new 数据类型[容量];
赋值号右侧:
new:
申请【XX】内存空间,并且清空整个内存空间中所有二进制位,所有的二进制位都是0
数据类型:
前后呼应,告知编译器这里支持存储的数据类型到底是什么?
[容量]:
容量==> Capacity
告知编译器,当前数组中能够存放的对应数据类型的数据,最大存储多少
个!!
【注意】
一旦确定容量,针对于当前数组,后期容量无法修改
- 注:数组是一种引用类型的变量,因此使用它定义一个变量时,仅仅表示定义了一个引用变量(也就是定义了一个指针),这个引用变量还未指向任何有效的内存,因此定义数组时不能指定数组的长度。而且由于定义数组只是定义了一个引用变量,并未指向任何有效的内存空间,所以还没有内存空间来存储数组元素,因此这个数组也不能使用,只有对数组进行初始化后才可以使用。
数组的初始化
Java语言中数组必须先初始化,然后才可以使用。所谓初始化,就是为数组的数组元素分配内存空间,并为每个数组元素赋初始值。
数组的初始化有如下两种方式。
-
静态初始化:初始化时由程序员显式指定每个数组元素的初始值,由系统决定数组长度。
//定义一个int数组类型的变量,变量名为intArr int[] intArr; //使用静态初始化,初始化数组时只指定数组元素的初始值,不指定数组长度 intArr = new int[]{5, 6,8, 20}; //定义一个object数组类型的变量,变量名为objArr object[] objArr; //使用静态初始化,初始化数组时数组元素的类型是 //定义数组时所指定的数组元素类型的子类 objArr = new String[]{"Java" ,"李刚"); Object[] objArr2; //使用静态初始化 objArr2=new Object[l ("Java" ,"李刚");
-
动态初始化:初始化时程序员只指定数组长度,由系统为数组元素分配初始值。
//数组的定义和初始化同时完成,使用动态初始化语法 int[] prices = new int[5] ; //数组的定义和初始化同时完成,初始化数组时元素的类型是定义数组时元素类型的子类 Object[] books = new String[4];
数组的下标
规定
数组中的下标是从0开始,到数组的容量 - 1
例如:
数组容量为10
有效下标范围: 0 ~ 9
超出有效下标范围,都是无效下标,后期考虑数组处理异常问题,负数使用问题
操作会出现的问题:
1. 数组下标越界
ArrayIndexOutOfBoundsException
数组和循环
获取数组容量的方式
数组名.length 属性
当前数组的属性length是要占用一定的数组空间的,属于数组中的内容,这就是为什么数组中占用的空间要比存储数据计算空间略大一些。
数组和方法
Java中如何数组成为方法的参数,已经使用方式
public static void main(String[] args) {
}
格式:
public static returnType methodName(arrayType[] arrayName)
arrayName是一个数组类型的变量,引用数据类型的变量
数组内存分析图
数组地址转移问题
Eclipse使用
1. 解压缩Eclipse.rar
2. 发送快捷方式到桌面
3. 选择自定义Workspace
4. Eclipse开发环境选择
5. 创建Java Project
a. 菜单栏 --> New -- > Java Project
b. Package Explorer --> 鼠标右键 --> New -- > Java Project
6. 怎么修改工作目录默认保存的位置
Eclipse开发环境选择
创建Java项目
创建Package
创建Java文件
Eclipse修改默认工作目录
Eclipse兼容性问题
Eclipse导入Java源代码
面向对象
类和对象
-
Java是面向对象的程序设计语言,类是面向对象的重要内容,可以把类当成-种自定义类型,可以使用类来定义变量,这种类型的变量统称为引用变量。也就是说,所有类是引用类型。
类: 对于一类事物的统称,对当前事物的一些描述,属性描述和行为描述 对象: 独立,唯一,特殊的个体 代码中: Arrays.toString(); Arrays.sort(); Arrays.binarySearch();
Java中定义类
- 对一个类定义而言,可以包含三种最常见的成员:构造器、成员变量和方法,三种成员都可以定义零个或多个,如果三种成员都只定义零个,就是定义了一个空类,这没有太大的实际意义。
类里各成员之间的定义顺序没有任何影响,各成员之间可以相互调用,但需要指出的是,static 修饰的成员不能访问没有static修饰的成员。
成员变量用于定义该类或该类的实例所包含的状态数据,方法则用于定义该类或该类的实例的行为特征或者功能实现。构造器用于构造该类的实例,Java语言通过new关键字来调用构造器,从而返回该类的实例。
构造器是一个类创建对象的根本途径,如果-一个 类没有构造器,这个类通常无法创建实例。因此,Java语言提供了一个功能:如果程序员没有为–个类编写构造器,则系统会为该类提供一个默认的构造器。一旦程序员为一个类提供了构造器,系统将不再为该类提供构造器。
定义类的格式
class ClassName {
// 属性描述
// 行为描述
}
要求:
1. ClassName要求符合大驼峰命名法,并且要做到见名知意!!!
2. 属性描述,是用于描述当前类拥有的一些特征,这里可以使用变量
该变量有一个特定的名字【成员变量】 Field
3. 行为描述,是用于描述当前类可以做到一些事情,这里使用方法。
该方法有一个特定的名字【成员方法】Method
成员变量
成员变量由英文单词field 意译而来,早期有些书籍将成员变量称为属性。但实际上在Java世界里属性(由property翻译而来)指的是一组setter 方法和getter方法。比如说某个类有age属性,意味着该类包含setAge()和getAge(两个方法。另外,也有些资料、书籍将field 翻译为字段、域。
/**
* 这里定义的是一个Person类
*
* @author Anonymous
*/
class Person {
// 属性描述,这里使用成员变量 Field
// 使用String类型来描述Person类的姓名属性
String name;
// 使用int类型数据描述Person类的年龄属性
int age;
// 使用char类型数据描述Person类的性别属性
char gender;
}
成员方法
// 使用方法描述Perosn类的吃饭行为
public void eat(String food) {
System.out.println("吃" + food);
}
// 使用方法描述Person类的睡觉行为
public void sleep() {
System.out.println("说出来你可能不行,是床先动的手~~~");
}
// 使用方法描述Person类的打豆豆行为
public void daDouDou() {
System.out.println("吃饭,睡觉,打豆豆");
}
/*
特征:
1. 目前没有使用static关键字
2. 方法和之前所学方式形式一致,同样拥有三大元素 返回值,方法名和形式参数列表
Shift + Alt + A
*/
类对象的形式
/*
new 对象!!!
*/
// 获取一个扫描器的类对象!!!
Scanner sc = new Scanner(System.in);
/*
java.util.Scanner :
数据类型,Scanner类型。创建的【变量】是Scanner的类对象,同时也是一个引用
数据类型
sc :
类对象,变量
Scanner的类对象,sc是对象名
new :
申请内存的【堆区】空间,并且清理整个空间中的所有数据。
代码中,只有有new关键字,一定会使用到内存的堆区空间,并且是新的内存空间。
Scanner(System.in) :
1. 这里是一个方法,因为有小括号
2. 方法名字和类名一致
3. 该方法称之为构造方法,Constructor 构造方法
总结:
类名 对象名 = new 构造方法(所需参数);
*/
// 这里创建了一个Person类对象,对象名person
Person person = new Person();
System.out.println(person);
/*
* com.qfedu.a_object.Person@15db9742
* com.qfedu.a_object 完整的包名
* Person 数据类型,这里创建的对象是一个Person类对象
* @15db9742 当前类对象在内存空间的中首地址!!!十六进制展示方式
*/
Person person1 = new Person();
System.out.println(person1);
/*
* com.qfedu.a_object.Person@6d06d69c
* 发现第二个Person类对象 person1空间首地址6d06d69c 和第一个对象不一致
* 这里就意味着两个对象的空间首地址不一致,不是同一个对象!!!
*/
类对象调用成员变量
/*
int[] arr = new int[10];
获取数组的容量:
arr.length
获取一个数组中【的】length属性
格式:
对象名.属性名/成员变量名;
. ==> 的
可以操作取值或者赋值操作。
*/
// 通过Person类对象 person调用类内的成员变量
// 【赋值】Person类对象 person中对应的成员变量
person.name = "";
person.age = ;
person.gender = '';
// 【取值】展示Person类对象 person中保存的成员变量数据
System.out.println("Name:" + person.name);
System.out.println("Age:" + person.age);
System.out.println("Gender:" + person.gender);
类对象调用成员方法
/*
得到了一个Scanner类对象sc
Scanner sc = new Scanner(System.in);
使用过以下方法:
sc.nextInt();
sc.nextFloat();
sc.nextLine().charAt(0);
格式:
类对象.方法名(必要的参数);
. ==> 的
*/
类对象内存分析图
类对象内存转移问题分析图
构造方法
问题:
发现在代码中没有显式定义构造方法,为什么可以使用一个无参数的构造方法?
Java反编译工具:
javap -c -l -private XXXX.class
使用构造方法
构造方法功能:
初始化当前类对象中保存的成员变量数据。
目前创建对象的方式;
new 构造方法(有可能需要的参数);
new:
1. 根据构造方法提供的数据类型申请对应的堆区内存空间。
2. 擦除整个空间中所有的数据。
构造方法:
初始化在当前内存堆区空间的成员变量对应的数据
格式:
public 类名(初始化形式参数列表) {
初始化赋值语句;
}
要求:
1. 无论什么时候一定要留有一个无参数构造方法备用
2. 根据所需情况完成构造方法参数选择
3. 一个class可以有多个构造方法【方法的重载】
多个构造方法
方法的重载
总结:
1. 所有的方法名字都是一致的!!!
2. 所有的方法参数都是不一样的!!!
3. 同一个类内!!!
这就是方法的重载!!!
优点:
1. 简化了开发压力
2. 简化了记忆压力
3. 更快捷的调用方法,同时又满足了不同的情况!!!
规范:
重载情况下,在同一个类内,不可以出现相同方法名和相同参数数据类型的方法!!!
基本原理:
方法名一致的情况下,通过形式参数列表数据类型的不同来选择不同的方法执行。
反射 ==>Constructor Method
this关键字
this关键字特征
package com.qfedu.a_object;
class SingleDog {
public SingleDog() {
System.out.println("Constructor : " + this);
}
public void test() {
System.out.println("Method Called : " + this);
}
}
/*
* this关键字特征:
* this关键字表示调用当前方法的类对象,
* 或者是当前构造方法中初始化的类对象
*/
public class Demo4 {
public static void main(String[] args) {
SingleDog singleDog = new SingleDog();
System.out.println("Instance : " + singleDog);
singleDog.test();
}
}
就近原则问题
/**
* 使用String类型参数和int类型参数初始化类对象成员变量数据
*
* @param name String类型数据 用于初始化name属性
* @param age int类型数据 用于初始化age属性
*/
public Cat(String name, int age) {
name = name;
age = age;
System.out.println("带有两个参数的构造方法");
}
/*
我们期望使用比较直观的参数名方式,告知调用者这里需要的数据到底是什么?
但是会导致【就近原则】问题
在构造方法中所有的name,age都会被看作是一个局部变量,而不是成员变量
期望:
可以有一种参数方式告知编译器这里操作的是一个成员变量,而不是一个局部变量!!!
*/
--------------------------修改方式----------------------------------
/**
* 使用String类型参数和int类型参数初始化类对象成员变量数据
*
* @param name String类型数据 用于初始化name属性
* @param age int类型数据 用于初始化age属性
*/
public Cat(String name, int age) {
/*
* 使用this关键字明确告知编译器这里使用的是一个成员变量,而不是
* 局部变量,解决就近原则问题
*/
this.name = name;
this.age = age;
}
成员变量和局部变量的对比
二分法查找
- 特征:
-
要求查询的数组必须是一个经过排序之后的数组
-
多个指定数据的情况下,无法告知到底是第几个数据出现
-
没有找到数据返回负数
/** * 二分法查找算法 * * @param sortedArray 这里要求是一个int类型数组,并且是进过排序之后的升序数组 * @param find 需要查询的元素 * @return 返回值大于等于0找到元素下标位置,没有找返回-1 */ public static int halfSearch(int[] sortedArray, int find) { int minIndex = 0; int maxIndex = sortedArray.length - 1; int mid = (minIndex + maxIndex) / 2; while (minIndex <= maxIndex) { if (sortedArray[mid] > find) { maxIndex = mid - 1; } else if (sortedArray[mid] < find) { minIndex = mid + 1; } else { return mid; } mid = (minIndex + maxIndex) / 2; } return -1; }
-
面向对象的三大特征
封装
- 封装(Encapsulation) 是面向对象的三大特征之一(另外两个 是继承和多态),它指的是将对象的状态信息隐藏在对象内部,不允许外部程序直接访问对象内部信息,而是通过该类所提供的方法来实现对内部信息的操作和访问。
封装是面向对象编程语言对客观世界的模拟,在客观世界里,对象的状态信息都被隐藏在对象内部,外界无法直接操作和修改。如Person对象的age变量,只能随着岁月的流逝,age才会增加,通常不能随意修改Person对象的age。对-一个类或对象实现良好的封装,可以实现以下目的。
JavaBean规范的类封装过程
-
代码规范非常重要 后期可以有效的提供我们的开发效率!!!
在Java中定义符合JavaBean规范的类有什么要求
- 所有的成员变量全部私有化 ==> private
- 必须提供一个无参数构造方法
- 要求使用private修饰的成员变量提供对应的操作方法 ==> Setter Getter
private关键字
private关键字是一个权限修饰符
private修饰的成员变量,成员方法,【构造方法】都是私有化内容,有且只能在类内使用,类外没有任何的操作权限!!!
Setter和Getter方法
private修饰的成员变量类外是没有任何操作权限,这里需要提供对应的操作方法,setter和getter方法
Setter方法格式:
public void set成员变量名(对应成员变量的数据类型 成员变量的形式参数) {
this.成员变量名 = 成员变量的形式参数;
}
例如:
public void setName(String name) {
this.name = name;
}
Getter方法格式:
public 对应成员变量的数据类型 get成员变量名() {
return 成员变量;
}
例如:
public String getName() {
return name;
}
如果成员变量是一个boolean类型,Getter方法有所不同
boolean married;
格式:
public boolean isMarried() {
return married;
}
匿名对象
Factory factory = new Factory();
// 通过修理店对象,调用修理电脑的方法
// 需要的参数是Computer类对象
factory.repair(computer);
/*
Factory类对象
1. 有且只使用了一次
2. 该对象中的设置操作是没有什么作用的
问题:
1. 代码操作感觉不方便
2. 浪费时间浪费资源,内存资源,JVM效率问题
*/
匿名对象:
new 构造方法(必要的参数);
匿名对象的用途:
1. 使用匿名对象直接调用类内的成员方法
2. 匿名对象直接作为方法的参数
注意:
使用匿名对象不要操作成员变量,有可能是有去无回
优势:
1. 阅后即焚,匿名对象在使用之后 立即被JVM GC收回
2. 解决内存空间,提高效率,简化代码书写
继承
-
Java的继承通过extends关键字来实现,实现继承的类被称为子类,被继承的类被称为父类,有的也称其为基类、超类。父类和子类的关系,是一种- -般和特殊的关系。 例如水果和苹果的关系,苹果继承了水果,苹果是水果的子类,则苹果是种特殊的水果。
-
因为子类是一一种特殊的父类,因此父类包含的范围总比子类包含的范围要大,所以可以认为父类是 大类,而子类是小类。
实现继承的方式
继承使用的关键字
extends
格式:
class A extends B {
}
A类是B类的一个子类
B类是A类的唯一父类
Java中的继承是一个单继承模式
基本要求:
1. 子类继承父类之后,可以使用父类的非私有化成员变量和成员方法
【非私有化成员】
2. 子类不能继承得到父类的私有化内容。
继承父类和子类内存分析
为什么会自动执行父类的无参数构造方法
这里有一个super关键字
1. 调用父类成员方法和成员变量的关键字。
[解决就近原则问题]
2. 用于显式调用父类的构造方法。
super关键字调用父类的构造方法
super(实际参数);
Java编译器会根据实际参数的数据类型,参数顺序,选择对应的父类构造方法执行,初始化父类的成员空间,方法重载机制。
特征:
1. 如果没有显式调用父类的构造方法,默认Java编译器会调用无参父类构造方法使用
2. 根据数据类型选择对应方法
3. super调用构造方法,必须在当前构造方法的第一行
方法的重写
解决的问题就是在开发中,父类的方法不适用于子类情况,子类可以重写父类的方法,完成自定义的方法使用
重写之后,在没有增加新的方法名的情况下,重写方法体内容,让方法满足子类,降低了开发压力,提高了效率。
@Override
严格格式检查
要求重写方法的和父类中的方法,声明完成一致,返回值类型,方法名和形式参数列表
重写和继承带来的问题
子类继承父类可以直接使用父类的方法,但是在这种情况下我们可以发现父类的方法是一定不能在子类中使用的,但是又没有一个强制要求。
需求:
强制要求子类重写父类的方法,从语法角度约束
abstract关键字
abstract修饰的方法
要求子类强制重写!!!
abstract使用总结:
1. abstract修饰的方法没有方法体
2. abstract修饰的方法必须定义在abstract修饰的类内或者interface接口内
3. 一个普通类【非abstract】修饰的类,继承了一个abstract类,那么必须实现在abstract类内的所有abstract,强制要求
4. 如果一个abstract A类继承另一个abstract B类,A类可以选择实现B类中abstract方法。
5. abstract修饰的类内允许普通方法
6. abstract修饰的类不能创建自己的类对象!!!
【原因】
abstract修饰的类内有可能存在abstract修饰的方法,而abstract修饰的方
法是么有方法体的,如果说创建了abstract修饰类对应的对象,不能执行没
有方法体的abstract方法
7. 一个类内没有abstract修饰的方法,那么这个类定义成abstract类有意义吗?
没有必要的!!!无意义的!!!
final关键字
最后的,最终的
final修饰的成员变量
final修饰的成员变量定义时必须初始化,并且赋值之后无法修改,一般用于类内带有名字的常量使用
final修饰的成员方法
final修饰的成员变量不能被子类重写,为最终方法,可以用于一些安全性方法的定义
final修饰的局部变量
final修饰的局部变量一旦被赋值,不能修改!
final修饰的类
final修饰的类没有子类,不能被继承。
abstract修饰的类不能被final修饰。
static关键字
static修饰成员变量
静态成员变量
1. 静态成员变量是使用static修饰的成员变量,定义在内存的【数据区】
2. 静态成员变量不推荐使用类对象调用,会提示警告
The static field SingleDog.info should be accessed in a static way
使用static修饰的SingleDog类内的info成员变量,应该通过静态方式访问
3. 静态成员变量使用类名调用是没有任何的问题。【墙裂推荐方式】
4. 在代码中没有创建对象时,可以通过类名直接使用静态成员变量,和【对象无关】
5. 代码中对象已经被JVM的GC销毁时,依然可以通过类名调用静态成员变量,和【对象无关】
6. 不管通过哪一种方式调用静态成员变量,修改对应的静态成员变量数据,所有使用到当前静态成员变量的位置,都会受到影响。
静态成员变量和对象无关
1. 从内存角度出发分析
静态成员变量是保存在内存的数据区
类对象占用的实际内存空间是在内存的堆区
这两个区域是完全不同的,所有可以说静态成员变量和对象没有关系 【没有对象】
2. 从静态成员变量以及类对象生命周期来分析
静态成员变量是随着类文件(.class) 字节码文件的加载过程中,直接定义在内存的数据区。静态成员变量从程序运行开始就已经存在。
类对象是在代码的运行过程中,有可能被创建的。程序的运行过中,有可能会被JVM的CG垃圾回收机制销毁,程序在退出之前一定会销毁掉当前Java程序使用到的所有内存。
静态成员变量在程序退出之后,才会销毁
静态成员变量的生命周期是从程序开始,到程序结束
类对象只是从创建开始,而且随时有可能被JVM的GC销毁
生命周期不在同一个时间线上,所以静态成员变量和类对象无关,【没有对象】
static修饰成员方法
静态成员方法的格式
异常熟悉的格式
public static 返回值类型 方法名(形式参数列表) {
}
静态成员方法注意事项
1. 静态成员方法推荐使用静态方式调用,通过类名调用【墙裂推荐的】
不推荐使用类对象调用,因为【没有对象】
2. 静态成员方法中不能使用非静态成员 ==> (非静态成员方法和非静态成员变量)
因为【没有对象】
3. 静态成员方法中不能使用this关键字
因为【没有对象】
4. 静态成员方法中可以使用类内的其他静态成员【难兄难弟】
5. 静态成员方法中可以通过new 构造方法创建对象
单身狗可以找对象
不能挖墙脚但是能自己找
静态成员方法特征解释
1. 静态成员方法加载时间问题
静态成员方法是随着.class字节码文件的加载而直接定义在内存的【方法区】,而且此时的静态成员方法已经可以直接运行。可以通过类名直接调用,而此时没有对象存在。【没有对象】
2. 为什么静态成员方法不能使用非静态成员
非静态成员变量和非静态成员方法时需要类对象调用的,在静态成员方法中,是可以通过类名直接执行的,而此时是【没有对象】的。
3. 为什么静态成员方法不能使用this关键字
this关键字表示的是调用当前方法的类对象,但是静态成员方法可以通过类名调用,this不能代表类名,同时也是【没有对象】
4. 静态成员方法可以使用其他静态成员
生命周期一致,调用方式一致
类变量和类方法
类变量 ==> 静态成员变量
类方法 ==> 静态成员方法
类成员 ==> 静态成员变量和静态成员方法
代码块
构造代码块
初始化当前类的所有类对象,只要调用构造方法,一定会执行对应的构造代码块
静态代码块
初始化程序,只要类文件加载,静态代码块中所有内容全部执行
局部代码块
提高效率,解决内存,让JVM回收内存的效率提升。
for () {
{
int num
}
}
static {
// 静态代码块
}
只要类文件加载,当前静态代码块中的内容就一定会执行,并且有且只执行一次。
整个类的初始化过程