Java(四)数组与类和对象
- 六、数组(非常重要)
- 1.定义
- 2.遍历
- 2.1遍历方法
- 2.2Arrays方法
- 3.二维数组
- 数组小总结
- 七、类和对象
- 1. 定义(重要)
- 1.1 类
- 1.2 对象
- 2. this关键字(重要)
- 2.1 特点
- 3. 构造方法(重要)
- 3.1实现方式
- 3.2作用
- 4. 封装
- 4.1包权限(重要)
- 4.2静态成员static(重要)
- 5.代码块
- 5.1 静态代码块(重要)
- 5.1同步代码块(多线程会出现)
- 6.内部类(重要)
- 6.1静态内部类
- 6.2非静态/实例内部类
- 6.3匿名内部类(不着急他的主要途径是在抽象类中)
- 类和对象的小总结
六、数组(非常重要)
1.定义
数组:可以看成是相同类型元素的一个集合。在内存中是一段连续的空间。
数组类型 [] 数组名 = new 数组类型[数组长度];
数组类型 [] 数组名={元素,元素};
int[] array1 = new int[10]; // 创建一个可以容纳10个int类型元素的数组
double[] array2 = new double[5]; // 创建一个可以容纳5个double类型元素的数组
String[] array3 = new double[3]; // 创建一个可以容纳3个字符串元素的数组
//数组初始化
int[] array1 = new int[]{0,1,2,3,4,5,6,7,8,9};
double[] array2 = new double[]{1.0, 2.0, 3.0, 4.0, 5.0};
String[] array3 = new String[]{"hell", "Java", "!!!"};
如果数组中存储元素类型为引用类型,默认值为null
2.遍历
- 数组是一段连续的内存空间,因此支持随机访问,即通过下标访问快速访问数组中任意位置的元素
- 下标从0开始,介于**[0, N)**之间不包含N,N为元素个数,不能越界,否则会报出下标越界异常。
(原因是在之前再说如何存储时说过,计算机寻找数据是通过地址访问,而在数组中在你定义一个恒定大小长度的数组,在内存中就会开辟与数据类型相应大小的容量,一旦发现你寻找的数据通过下标寻找不在数组范围内就会出错(报错)。)
程序计数器 (PC Register): 只是一个很小的空间, 保存下一条执行的指令的地址
虚拟机栈(JVM Stack): 与方法调用相关的一些信息,每个方法在执行时,都会先创建一个栈帧,栈帧中包含有:局部变量表、操作数栈、动态链接、返回地址以及其他的一些信息,保存的都是与方法执行时相关的一些信息。比如:局部变量。当方法运行结束后,栈帧就被销毁了,即栈帧中保存的数据也被销毁了。
本地方法栈(Native Method Stack): 本地方法栈与虚拟机栈的作用类似. 只不过保存的内容是Native方法的局部变量. 在有些版本的 JVM 实现中(例如HotSpot), 本地方法栈和虚拟机栈是一起的
堆(Heap): JVM所管理的最大内存区域. 使用 new 创建的对象都是在堆上保存 (例如前面的 new int[]{1, 2,3} ),堆是随着程序开始运行时而创建,随着程序的退出而销毁,堆中的数据只要还有在使用,就不会被销毁。
方法区(Method Area): 用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据. 方法编译出的的字节码就是保存在这个区域
int[] array = {1, 2, 3};
System.out.println(array[3]); // 数组中只有3个元素,
//下标一次为:0 1 2,array[3]下标越界
2.1遍历方法
通过循环遍历数组
int[]array = new int[]{10, 20, 30, 40, 50};
for(int i = 0; i < 5; i++){
System.out.println(array[i]);
}
注意:在数组中可以通过 数组对象.length 来获取数组的长度
int[]array = new int[]{10, 20, 30, 40, 50};
for(int i = 0; i < array.length; i++){
System.out.println(array[i]);
}
for循环的强化
int[] array = {1, 2, 3};
for (int x : array) {
System.out.println(x);
}
基本变量:基本数据类型创建的变量,称为基本变量,该变量空间中直接存放的是其所对应的值
引用类型变量:而引用数据类型创建的变量,一般称为对象的引用,其空间中存储的是对象所在空间的地址
方法参数为引用类型,这就可以通过地址改动或调用数组中的元素
public static void main(String[] args) {
int[] arr = {1, 2, 3};
func(arr);
System.out.println("arr[0] = " + arr[0]);
}
public static void func(int[] a) {
a[0] = 10;
System.out.println("a[0] = " + a[0]);
}
2.2Arrays方法
3.二维数组
数据类型[][] 数组名称 = new 数据类型 [行数][列数] { 初始化数据 };
int[][] arr = {{1, 2, 3, 4},
{5, 6, 7, 8},
{9, 10, 11, 12}
};
二维数组遍历
for (int row = 0; row < arr.length; row++) {
for (int col = 0; col < arr[row].length; col++) {
System.out.printf("%d\t", arr[row][col]);
}
System.out.println("");
}
其实就是一个特殊的一维数组。
数组小总结
1.数组是引用类型(通过地址寻找)
2.下标从0~数组长度-1
3.数组中元素类型与数组类型一致
七、类和对象
1. 定义(重要)
类是用来对一个实体(对象)来进行描述的,比如一个人(实体(对象)),但是一个人又有脖子(类),手(类),脚(类),眼睛(类)等,这样我们就对一个人进行了完整的刻画。当然人(对象)也是不同的。比如,我和你都是人,但是我们脖子或许有些不同。
1.1 类
主要描述该实体(对象)具有哪些属性(外观尺寸等)。
类中包含的内容称为类的成员。
属性主要是用来描述类的,称之为类的成员属性或者类成员变量。
方法主要说明类具有哪些功能,称为类的成员方法。
class PetDog {
public String name;//名字
public String color;//颜色
// 狗的属性
public void barks() {
System.out.println(name + ": 旺旺旺~~~");
}
// 狗的行为
public void wag() {
System.out.println(name + ": 摇尾巴~~~");
}
}
- 一般一个文件当中只定义一个类
- main方法所在的类一般要使用public修饰
- public修饰的类必须要和文件名相同
- 不要轻易去修改public修饰的类的名称
1.2 对象
new 关键字用于创建一个对象的实例. 使用 . 来访问对象中的属性和方法. 同一个类可以创建多个实例.
简单而言就是,人不同,但描述人的方式是一样的。通过不同的实例化从而进行对不同人的描述。
class PetDog {
public String name;//名字
public String color;//颜色
// 狗的属性
public void barks() {
System.out.println(name + ": 旺旺旺~~~");
}
// 狗的行为
public void wag() {
System.out.println(name + ": 摇尾巴~~~");
}
}
public class Main{
public static void main(String[] args) {
PetDog dogh = new PetDog(); //通过new实例化对象
dogh.name = "阿黄";
dogh.color = "黑黄";
dogh.barks();
dogh.wag();
PetDog dogs = new PetDog();
dogs.name = "阿黄";
dogs.color = "黑黄";
dogs.barks();
dogs.wag();
}
}
2. this关键字(重要)
在实际运用中,会出现一个方法会传入多种数据的情况,但是每个传进去的值,一定不能拿错。为了确定每个传进去的值,就用this关键字。
具体问题是:
- 形参名不小心与成员变量名相同
- 多个对象都在调用相同的函数,但是这这些函数中没有任何有关对象的说明,这些函数如何知道打印的是那个对象的数据呢?
具体作用:this引用指向当前对象(成员方法运行时调用该成员方法的对象),在成员方法中所有成员变量的操作,都是通过该引用去访问。
一个时间例子:
public class Date {
public int year;
public int month;
public int day;
public void setDay(int y, int m, int d){
//多个数据传入后根本无法确定传入的值是什么。
year = y;
month = m;
day = d;
}
public void printDate(){
System.out.println(year + "/" + month + "/" + day);
}
public static void main(String[] args) {
// 构造三个日期类型的对象 d1 d2 d3
Date d1 = new Date();
Date d2 = new Date();
Date d3 = new Date();
// 对d1,d2,d3的日期设置
d1.setDay(2020,9,15);
d2.setDay(2020,9,16);
d3.setDay(2020,9,17);
// 打印日期中的内容
d1.printDate();
d2.printDate();
d3.printDate();
}
}
正确写法
创建一个时间对象。
public class Date {
public int year;
public int month;
public int day;
public void setDay(int year, int month, int day){
this.year = year;
this.month = month;
this.day = day;
}
public void printDate(){
System.out.println(this.year + "/" + this.month + "/" + this.day);
}
}
调用
public static void main(String[] args) {
Date d = new Date();
d.setDay(2020,9,15);
d.printDate();
}
2.1 特点
- this的类型:对应类类型引用,即哪个对象调用就是哪个对象的引用类型
- this只能在成员方法中使用
- 在成员方法中,this只能引用当前对象,不能再引用其他对象
- this是成员方法第一个隐藏的参数,编译器会自动传递,在成员方法执行时,编译器会负责将调用成员方法对象的引用传递给该成员方法,this负责来接收
3. 构造方法(重要)
构造方法(也称为构造器)是一个特殊的成员方法,名字必须与类名相同,在创建对象时,由编译器自动调用,并且在整个对象的生命周期内只调用一次。
3.1实现方式
- 名字与类名相同,没有返回值类型,设置为void也不行
- 一般情况下使用public修饰
- 在创建对象时由编译器自动调用,并且在对象的生命周期内只调用一次
public class Date {
public int year;
public int month;
public int day;
// 构造方法:
public Date(int year, int month, int day){
this.year = year;
this.month = month;
this.day = day;
System.out.println("Date(int,int,int)方法被调用了");
}
public void printDate(){
System.out.println(year + "-" + month + "-" + day);
}
public static void main(String[] args) {
// 此处创建了一个Date类型的对象,并没有显式调用构造方法
Date d = new Date(2021,6,9); // 输出Date(int,int,int)方法被调用了
d.printDate(); // 2021-6-9
}
}
3.2作用
构造方法的作用就是对对象中的成员进行初始化,并不负责给对象开辟空间。
特点:
- 名字必须与类名相同
- 没有返回值类型,设置为void也不行
- 创建对象时由编译器自动调用,并且在对象的生命周期内只调用一次(相当于人的出生,每个人只能出生一次)
- 构造方法可以重载(用户根据自己的需求提供不同参数的构造方法)(名字相同,参数列表不同,因此构成了方法重载)
- 如果用户没有显式定义,编译器会生成一份默认的构造方法,生成的默认构造方法一定是无参的。
- 没有定义任何构造方法,编译器会默认生成一个不带参数的构造方法。
对象空间被申请好之后,对象中包含的成员已经设置好了初始值,
数据类型 | 默认值 |
byte | 0 |
char | ‘\u0000’ |
short | 0 |
int | 0 |
long | 0L |
boolean | false |
float | 0.0f |
double | 0.0 |
reference | null |
4. 封装
面向对象程序三大特性:封装、继承、多态。而类和对象阶段,主要研究的就是封装特性。何为封装呢?简单来说就是套壳屏蔽细节。
举个例子说明:
我们在使用手机时我们用户只管直接去使用,无需去理解这些具体功能是如何实现的。我只需要来用。但是对于设计者而言。他们需要去将这些功能进行实现,并将这些功能装起来,无需用户去理解功能实现,只需要他们会用就可以。
4.1包权限(重要)
范围 | private | default | protected | public |
同一包中的同一类 | ✓ | ✓ | ✓ | ✓ |
同一包中的不同类 | × | ✓ | ✓ | ✓ |
不同包中的子类 | × | × | ✓ | ✓ |
不同包中的非子类 | × | × | × | ✓ |
public:可以理解为一个人的外貌特征,谁都可以看得到
default: 对于自己家族中(同一个包(同一个目录)中)不是什么秘密,对于其他人来说就是隐私了
private:只有自己知道,其他人都不知道
在面向对象体系中,提出了一个软件包的概念,即:为了更好的管理类,把多个类收集在一起成为一组,称为软件包。有点类似于目录。
可以使用 import语句导入包
import java.util.*;
public class Computer {
private String cpu; // cpu
private String memory; // 内存
public String screen; // 屏幕
String brand; // 品牌---->default属性
public Computer(String brand, String cpu, String memory, String screen) {
this.brand = brand;
this.cpu = cpu;
this.memory = memory;
this.screen = screen;
}
public void Boot(){
System.out.println("开机~~~");
}
public void PowerOff(){
System.out.println("关机~~~");
}
public void SurfInternet(){
System.out.println("上网~~~");
}
}
public class TestComputer {
public static void main(String[] args) {
Computer p = new Computer("HW", "i7", "8G", "13*14");
System.out.println(p.brand); // default属性:只能被本包中类访问
System.out.println(p.screen); // public属性: 可以任何其他类访问
// System.out.println(p.cpu); // private属性:只能在Computer类中访问,不能被其他类访问
}
}
通过import 导入我们需要的包。可以通过 * 号将整个包导入,也可以将这个符号改成具体的文件名。我更建议后者。因为这个符号代表者文件下面的全部文件。我们更建议显式的指定要导入的类名. 否则还是容易出现冲突的情况.
常见的包:
- java.lang:系统常用基础类(String、Object),此包从JDK1.1后自动导入。
- java.lang.reflect:java 反射编程包;
- java.net:进行网络编程开发包。
- java.sql:进行数据库开发的支持包。
- java.util:是java提供的工具程序包。(集合类等) 非常重要
- java.io:I/O编程开发包。
4.2静态成员static(重要)
static修饰的成员变量,称为静态成员变量,静态成员变量最大的特性:不属于某个具体的对象,是所有对象所共享的。(俗称静态区)
- 不属于某个具体的对象,是类的属性,所有对象共享的,不存储在某个对象的空间中
- 既可以通过对象访问,也可以通过类名访问,但一般更推荐使用类名访问
- 类变量存储在方法区当中
- 生命周期伴随类的一生(即:随类的加载而创建,随类的卸载而销毁)
public class Student{
private String name;
private String gender;
private int age;
private double score;
private static String classRoom = "Bit306";//看清楚,这里我用的是private修饰
// ...
}
public class TestStudent {
public static void main(String[] args) {
System.out.println(Student.classRoom);
}
}
这个代码会出现编译错误。
正确的是:
public class Student{
private String name;
private String gender;
private int age;
private double score;
private static String classRoom = "Bit306";
public static String getClassRoom(){
return classRoom;
}
}
public class TestStudent {
public static void main(String[] args) {
System.out.println(Student.getClassRoom());
}
}
5.代码块
使用 {} 定义的一段代码称为代码块。
public class Main{
public static void main(String[] args) {
{ //直接使用{}定义,普通方法块
int x = 10 ;
System.out.println("x1 = " +x);
}
int x = 100 ;
System.out.println("x2 = " +x);
}
}
实例代码块。构造代码块一般用于初始化实例成员变量。
private String name;
private String gender;
private int age;
private double score;
public Student() {
System.out.println("I am Student init()!");
}
//实例代码块
{
this.name = "bit";
this.age = 12;
this.sex = "man";
System.out.println("I am instance init()!");
}
5.1 静态代码块(重要)
使用static定义的代码块称为静态代码块。一般用于初始化静态成员变量。
public class Student{
private String name;
private String gender;
private int age;
private double score;
private static String classRoom;
//实例代码块
{
this.name = "李思";
this.age = 12;
this.gender = "men";
System.out.println("你好!");
}
// 静态代码块
static {
classRoom = "6班";
System.out.println("你好同学!");
}
- 静态代码块不管生成多少个对象,其只会执行一次
- 静态成员变量是类的属性,因此是在JVM加载类时开辟空间并初始化的
- 如果一个类中包含多个静态代码块,在编译代码时,编译器会按照定义的先后次序依次执行合并
- 实例代码块只有在创建对象时才会执行
5.1同步代码块(多线程会出现)
等待我后续文章,这里不多解释,建议先看我后面的数据结构在来阅读这方面的文章.
6.内部类(重要)
A是外部类
B是内部类
可以将一个类定义在另一个类或者一个方法的内部,前者称为内部类,后者称为外部类。内部类也是封装的一种体现。(简单而言就是,在类中定义一个类,简称类中类)(调用方法在静态类中有体现)
public class A{
class B{
}
}
- 外部类中的任何成员都可以在实例内部类方法中直接访问
- 实例内部类所处的位置与外部类成员位置相同,因此也受public、private等访问限定符的约束
- 在实例内部类方法中访问同名的成员时,优先访问自己的,如果要访问外部类同名的成员,必须:外部类名
称.this.同名成员 来访问 - 实例内部类对象必须在先有外部类对象前提下才能创建
- 实例内部类的非静态方法中包含了一个指向外部类对象的引用
- 外部类中,不能直接访问实例内部类中的成员,如果要访问必须先要创建内部类的对象。
public class A{
// 成员位置定义:未被static修饰 --->实例内部类
public class B{
}
// 成员位置定义:被static修饰 ---> 静态内部类
static class C{
}
public void func(){
// 方法中也可以定义内部类 ---> 局部内部类:几乎不用
class D{
}
}
}
6.1静态内部类
被static修饰的内部成员类称为静态内部类
public class ClassA {
private int a;
static int b;
public void methodA(){
a = 10;
System.out.println(a);
}
public static void methodB(){
System.out.println(b);
}
// 静态内部类:被static修饰的成员内部类
static class ClassB{
public void methodInner(){
// 在内部类中只能访问外部类的静态成员
// a = 100; // 编译失败,因为a不是类成员变量
b =200;
// methodA(); // 编译失败,因为methodB()不是类成员方法
methodB();
}
}
public static void main(String[] args) {
// 静态内部类对象创建 & 成员访问
ClassA.ClassB Class = new ClassA.ClassB();
Class.methodInner();
}
}
6.2非静态/实例内部类
即未被static修饰的成员内部类。
public class OutClass {
private int a;
static int b;
int c;
public void methodA(){
a = 10;
System.out.println(a);
}
public static void methodB(){
System.out.println(b);
}
// 实例内部类:未被static修饰
class InnerClass{
int c;
public void methodInner(){
// 在实例内部类中可以直接访问外部类中:任意访问限定符修饰的成员
a = 100;
b =200;
methodA();
methodB();
// 如果外部类和实例内部类中具有相同名称成员时,优先访问的是内部类自己的
c = 300;
System.out.println(c);
// 如果要访问外部类同名成员时候,必须:外部类名称.this.同名成员名字
OutClass.this.c = 400;
System.out.println(OutClass.this.c);
}
}
public static void main(String[] args) {
// 外部类:对象创建 以及 成员访问
OutClass outClass = new OutClass();
System.out.println(outClass.a);
outClass.methodA();
实例内部类的访问
// 要访问实例内部类中成员,必须要创建实例内部类的对象
// 而普通内部类定义与外部类成员定义位置相同,因此创建实例内部类对象时必须借助外部类
// 创建实例内部类对象
OutClass.InnerClass innerlass1 = new OutClass().new InnerClass();
// 上述语法比较怪异,也可以先将外部类对象先创建出来,然后再创建实例内部类对象
OutClass.InnerClass innerClass2 = outClass.new InnerClass();
innerClass2.methodInner();
}
6.3匿名内部类(不着急他的主要途径是在抽象类中)
类和对象的小总结
1.类就是描述物体的一种集合,类中定义的方法以及成员是具体描述这个物体的固有属性(称其为对象)(如动物这个大类他是一个抽象的描述,并不具体,而动物有很多他们都有不同的名字,体型,身高,叫声,比如狗,猫,鸟,老虎…这些动物的描述方式各有不同)
2.用{}包裹的就是代码块,用static修饰的是静态代码块
3.内部类就是通过在一个类中再次定义一个的方式,通常分为非静态内部类,静态内部类(用static修饰的静态的内部类。)
4.内部类调用通常:(非静态内部类 ) **外部类.内部类 xxx=外部类.new 内部类();**进行实例化。(静态内部类)**外部类.内部类 xxx=new 外部类.内部类();**进行实例化。