数组
数组的使用
数组可以存放多个同一类型的数据,数组也是一种数据类型,是引用类型。即:数组就是一组数据
问题引入
传统的解决方式
使用数组来解决
可以看到,我们创建了一个double类型元素的数组,将我们要计算的数据都保存在了这个数组中,然后通过for循环来遍历每一个值,每次循环体都让保存总和的变量加等于目前遍历的值。
数组是通过数组引用变量+[下标]来访问元素值的,数组元素值是从0开始的,我们通过将循环变量传入[]中作为下标来循环遍历数组中的每一个元素,达到取值运算。
数组的定义
数组分两类定义:静态初始化和动态初始化
动态初始化数组数组定义格式:
数据类型 数组名[] = new 数据类型[大小]
动态初始化由程序员指定内存大小,程序为这些元素赋值,赋值是各个类型的默认值,比如整数就是0,引用类型是null,浮点型是0.0等等。
静态初始化数组数组定义格式:
数组类型 数组名[] = {元素元素};
静态初始化是程序员指定元素列表,程序为数组定义元素列表所需要的数组大小。
数组练习
练习1
练习2
该练习的思路就是,在循环外层设置出数组的第一个元素以及索引,然后通过for循环来挨个和从下标1(第二个元素)开始的数组元素进行比较,如果循环外层的值小于当前比较的元素值,就将当前元素值赋值给外层的值,然后依次比较,索引则通过循环变量i来获取。
数组赋值的机制
基本类型的赋值机制
数组引用类型的赋值机制
可以看到,按照地址传递的机制,数组1将地址赋值给数组2后,数组2修改了数组2的值,但是会发现数组1的值也发生了改变,这就是地址赋值,指的是内存地址的赋值,两个引用变量同时指向了数组1所指向的堆内存的数据,数组2修改数据时,修改的就是这个堆内存汇总的数据,所致导致不论是数组1修改还是数组2修改,都会发生变化。
基本数据类型值传递机制
数组引用类型内存地址传递
总结
基本数据类型和引用数据类型的变量都存放在栈内存中,但是基本数据类型并不会开辟堆内存空间,所以它们的值也只是在栈内存中,当进行赋值和值传递时,都是直接替换和复制值;而引用数据类型在栈内存中保存的都只是内存地址,该内存地址真正代表的数据在堆内存中,所以如果是引用类型进行值传递,那么一定是内存地址的传递,所以相当于它们进行值传递后,就共享了同一个内存地址的数据(堆内存中的数据)。
一维数组的一些基础操作
数组拷贝
根据上面的学习,我们可以看到,如果只是单纯的arr1=arr2,这样它们的本质并没有生成新的数组,而只是一个内存区有了两个引用,如果我们想真正的生成一个新数组,则应该是实现数组拷贝(内容复制ArrayCopy)。
数组反转
如果觉得上面的复杂也是可以反向遍历数组然后再创建新数组,int i=arr.length-1;i>=0;i–;
数组扩容
直接进行下标扩容是会报错的。
动态扩容数组
Scanner myScanner = new Scanner(System.in);
//定义数组
int arr[] = {1,2,3};
do{
int arrNew[] = new int[arr.length+1];
for(int i=0;i<arr.length;i++){
arrNew[i] = arr[i];
}
System.out.println("请输入你要添加的元素");
//把4赋给arrNew最后一个元素
arrNew[arrNew.length-1] = myScanner.nextInt();
arr = arrNew;//经过引用重定向,原本堆内存的数据会被回收
for(int i:arr){
System.out.print(i+"\t");
}
//问用户是否继续
System.out.println("是否继续添加 y/n");
char key = myScanner.next().charAt(0);
if(key == 'n'){//如果输入N就结束了
break;//结束循环
}
}while(true);
System.out.println("已退出...");
数组的基本排序
冒泡排序
public class Array{
public static void main(String[] args) {
int arr[] = {24,69,80,57,13};
for(int i = 0;i<arr.length-1;i++){//外层循环次数
int temp=0;//临时变量
for(int j=0;j<arr.length-1-i;j++){
//如果前面的数>后面的数,就交换
if(arr[j]>arr[j+1]){
temp = arr[j];
arr[j] = arr[j+1];
arr[j+1] = temp;
}
}
System.out.println("==第"+(i+1)+"轮排序==");
for(int j=0;j<arr.length;j++){
System.out.print(arr[j]+"\t");
}
System.out.println();
}
}
}
/*冒泡排序思路总结
首先里层的遍历是判断当前元素是否大于后一个元素值
因为每次比较都向前走,所以循环次数也是要每次减少的
外层循环则等于一共要循环的次数
*/
查找
顺序查找
import java.util.Scanner;
public class Array01 {
public static void main(String[] args) {
/*
有一个数列:凯迪拉克、奥迪、宝马、奔驰、长安
从键盘中任意输入一个名称,判断数列中是否包含次名称【循环查找】
要求:如果找到了,就提示找到,并给出下标值
思路分析:
1.定义一个字符串数组
2.接收用户输入,遍历数组,逐一比较,如果有,则提示信息,并退出
*/
String strArr[] = {"长安15","凯迪拉克25","奥迪35","宝马45","奔驰55"};
Scanner sc = new Scanner(System.in);
System.out.println("请输入品牌");
String findName = sc.next();
//遍历数组,逐一比较,如果有,则提示信息,并退出
int index = -1;
for(int i=0;i<strArr.length;i++){
//比较 字符串比较 equals,如果要找的品牌,就是当前元素
if(findName.equals(strArr[i])){
System.out.println("已找到");
System.out.println("恭喜你称为尊贵的 "+strArr[i]+" 车主");
System.out.println("耗时: "+i+" 年");
//把i保存到index
index = i;
break;//退出
}
}
if(index==-1){
System.out.println("对不起没有成为任何品牌的车主,继续努力!");
}
}
}
二维数组入门
输入二维棋盘
二维数组的使用
//一共有三个一维数组,每个一维数组的元素是不一样的
int arr[][] = new int[3][];
for(int i=0;i<arr.length;i++){//遍历arr每个一维数组
//给每个一维数组开空间 new
arr[i] = new int[i+1];
//遍历一维数组,并给一维数组的每个元素赋值
for(int j=0;j<arr[i].length;j++){
arr[i][j] = i+1;
}
}
//遍历arr输出
for(int i=0;i<arr.length;i++){
for(int j = 0;j<arr[i].length;j++){
System.out.print(arr[i][j]+"\t");
}
System.out.println();
}
面向对象OOP
类与对象的引出
问题
封装
private修饰的成员属性是不能直接通过对象实例访问的,如果要访问则需要定义setter和getter方法。
多态
多态引入
方法多态
对象的多态
父类 父类引用 = new 子类();
编译类型是父类,但是运行类型是子类。
结合上面的类多态,我们可以解决前面案例的问题
单例模式
饿汉式
class Cat{
//直接就在类的内部创建了
//即使没有使用,cat对象也被加载了
private String name;
//添加static的原因是,如果不是静态方法获取,那么别的类是需要创建对象才能调用该方法的
private static Cat cat = new Cat("?");
//保障只有一个GirlFriend对象[饿汉式单例]
//1.私有化构造器
//2.在类的内部直接创建
//3.提供一个公共的静态方法,返回gf对象
private Cat(String name) {
this.name = name;
}
public static Cat getInstance(){
return cat;
}
}
懒汉式
懒汉式和饿汉式的区别就是延迟加载的区别。
//程序运行过程中,只能创建一个Cat对象
class Cat{
private String name;
private static Cat cat;//这样尽管是外围调用类属性,也不会调用Cat构造器创建对象
//步骤
//1.创建私有化构造器
//2.定义一个static静态对象
//3.提供一个公共的static方法,可以返回一个Cat对象
private Cat(String name){
this.name=name;
}
public static Cat getInstance(){
if(cat == null){
cat = new Cat("斯芬克斯");
}
}
}
多次调用单例方法也并不会二次调用构造器。