-
什么是Java
Java是一门面向对象的编程语言
面向对象:一个很抽象的专业术语,暂时不用管。
编程语言:计算机可以识别的语言。
-
Java的优势
- 需求范围广,网站,微信小程序,手机APP等都是Java作为主要语言编写。
- 学习成本低,公认最简单的高级语言。
-
Java的专业术语
JVM:Java虚拟机,运行Java的必备工具
JRE: Java运行环境,运行Java的必备环境
JDK: Java的开发工具包
-
Java的三大版本
- JavaSE Java基础版 重中之重
- JAVAEE Java进阶版 做网站的必备技术
- JAVAME Java进阶版 已过时,只需要知道它的存在,不需要知道怎么用。
-
Java的开发工具
开发工具 | 优点 | 缺点 |
Eclipse | 免费,简单 | 功能少 |
Idea | 功能多 | 收费,使用复杂,破解复杂 |
新手适合Eclipse,高手适合Idea
新手:掌握程度在java面向对象设计,JavaWeb程序设计
高手:SSM框架,SpringBoot框架
-
Eclipse项目的目录说明
第一层:project 表示项目名,例如goods_manage_system(商品管理) demo2201(演示2201)
第二层:JRE(Java的必备运行环境)和src(存放源代码的地方)
第三层:package(包),方便分类管理若干个源代码文件,一般使用公司域名倒写。
第四层:类,.java后缀的文件,java程序的源代码
-
命名规范
项目名:纯小写,不包含中文。多个单词用下划线隔开即可
包名:公司域名倒写。例如com.yyzy.test
类名:大驼峰命名法。每个单词的首字母均大写。多个单词直接拼接,通过大写区分。
例如:GoodsPrice 商品价格。
-
Java注释的分类
什么是注释:方便程序员阅读理解代码的用处。注释不会被java运行。
意思就是:写了仅仅是给程序员看的。
Java有3个类型的注释。
1.单行注释。把当前行注释掉 例如: // 注释内容
2.多行注释。把多行都注释。虽然可以使用多个单行注释来代替 /* */
3.文档注释。和多行注释类似,用途较少,一般是写文档用的 /** */
-
解释第一个源文件的相关意义
package com.yyzy.test; //上述一行代码,是每个类(一个Java源文件)都必备的 //表示这个类在哪个包(路径)下 /* 每个类的模板都是以下格式:其中Xxx必须和类名(源文件名)保持一致 public class Xxx { //若干代码 } */ // Java源代码的运行入口(从哪里开始运行):都是如下格式: /* public static void main(String[] args) { //这里的第一行就是运行的第一行代码 } //以上方法又称为:main方法 */ //System.out.println("我的第一个Java程序"); 表示打印制定的内容 //如果是字符记得加双引号 public class Test1 { public static void main(String[] args) { System.out.println("我的第一个Java程序"); } } |
课堂练习,创建一个类。要求运行该类,在控制台打印5句
package com.yyzy.test; public class Test2 { public static void main(String[] args) { //快捷键1:alt + / 表示根据你的输出内容进行对应的提示 //例如输入main 就提示入口函数 //例如输入syso 就提示打印输出代码 System.out.println("我是汉字所有加双引号"); System.out.println("i am english so 也要加双引号"); System.out.println(12345);//我是数字就不需要加了 } } |
-
讲解数据类型的含义
例如:有如下数据,每个数据的类型都不一样
昵称:独孤求败 等级:120级 点券余额:25000 身高:180.5
性别:男
所以Java针对不同的数据也有不同的语法来声明不同的数据。
-
讲解Java的8大基本数据类型
数字类型: byte short int long
字符类型: char
布尔类型: boolean
-
如何声明一个数据
- 记得在main方法里面写。
- 语法: 数据类型 变量名 = 变量值;
- 变量名的命名规范:小驼峰命名法:首字母小写。后续每个单词的首字母大写。
//如何声明数据: 语法: 数据类型 变量名 = 变量值; //例如声明一个年龄为18岁 byte abc = 18;//abc表示我的年龄 short age2 = 18; int age3 = 18; long age4 = 18; //字符类型:只能声明单个字符。用单引号声明即可 //例如声明性别: char sex = '男'; //例如钱包余额 如果用float存放小数,记得在后面补上f float money = 11.5f; double money123 = 115.55; //布尔类型:只能存放true或false 不用加引号 //你是猪吗? boolean isPig = true; //重点掌握:int char double boolean //原因:整数类型int 绰绰有余 //char:8个基本数据类型只有char可以存储字符(汉字 字母) //double:存储的范围比float大。且更加精确 //boolean:只有boolean可以存储布尔类型 |
java数据基本类型
JAVA中数据分为两大类:
A.8大基本数据类型
B.引用数据类型 除了8大基本数据类型之外的都称之为引用数据类型
例如:String Array(数组) Scanner 等等。
方法的组成
方法的组成: public void a(int a1,String b2){}
调用方法: a(10,"你好");
实参和形参的区别:
声明方法的参数列表叫做形参。例如a1 b2
调用方法的参数列表叫做实参。例如10 "你好"
讨论:如果形参是基本数据类型或引用数据类型 区别是什么?
如果基本数据类型作为形参,那么不会改变实参的值。
如果引用数据类型作为形参,那么会改变实参的值。
*/
-
常见的运算符
//java中常见的运算符 //1.赋值运算符 = 将右边的值赋值给左边 // int a = 123; // int b = a; // System.out.println(a); // System.out.println(b); //算术运算符 + - * / // int a = 10; // int b = 11; // System.out.println(a+b); //算术运算符 % 取余数 // System.out.println(10%3);//1 //算术运算符 ++ -- //语法 a++ 和 a-- 表示先用(执行)在加或减 // int a = 10; // a++; // System.out.println(a);//11 // int a = 10; // System.out.println(a++);//10 //语法: --a ++a 先加减再用 // int a = 10; // --a; // System.out.println(a);//9 // int b = 10; // ++b; // System.out.println(b);//11 //关系运算符: //> >= < <= == != 他们的返回值都是boolean类型 // int a = 10; // int b = 9; // System.out.println(a>b); //逻辑运算符 && 短路与 || 短路或 // 语法如下: 表达式1 && 表达式2 //两个表达式都是true 才返回true //如果一个为false 那么结果就是false //运算从左到右。所以如果第一个式子为false 将不会执行第二个式子。 // int a = 10; // int b = 9 ; // System.out.println(a>b && b<a);//true // System.out.println(a>b && b++<a);//true // || 短路或 // 语法如下: 表达式1 || 表达式2 //两个表达式都是false 才返回false //如果一个为true 那么结果就是true //运算从左到右。所以如果第一个式子为true 将不会执行第二个式子。 //& 和 | : //和上述用法完全一致,唯一区别:没有短路功能 //&和| 左右两边的式子都会执行 //例题: // int a = 10; // int b = 9; // System.out.println(a<b && a++>10 );//false // System.out.println(a); // int a = 10; // int b = 9; // System.out.println(a<b & a++>10 );//false // System.out.println(a); //三元表达式:语法:表达式1?值1:值2 //表示:如果表达式1为true就是值1 反之就是值2 // int age = 20; // System.out.println(age>18?"成年":"未成年"); |
什么是类
1.凡是一个.java后缀修饰的文件都是一个类
2.每个java文件中可以存在多个类。语法如下 class Xxx { }
3.每个Java文件有且只能有一个被public修饰的类,且必须和文件名一致。
一般来说:类的组成:A.属性 B.方法
封装性
面向对象特征之一:封装性
1.为什么需要封装?
理论上:
-高内聚:类的内部数据操作细节自己完成,不允许外部干涉
-低耦合:仅暴露少量的方法给外部使用,尽量方便外部调用
通俗的说:把该隐藏的隐藏起来,该暴露的暴露出来。
2.如何实现数据封装?
2.1 权限修饰符
java规定了4种权限修饰符,分别是:private、缺省、protected、public
本类内部(private)、本包内(缺省)、其他包的子类(protected)、其他包非子类(public)
每一个修饰符所在位置,包含前一个的范围
2.2 作用
我们可以使用4种权限修饰符来修饰类及类的内部成员。当这些成员被调用时,体现可见性的大小
2.3 实际案例:
在题目中给Animal的对象legs属性赋值。在实际常识中,legs不能为负数,如果直接调用属性legs
不能加入判断逻辑,解决如下:
-将legs属性私有化(private),禁止在Animal类的外部直接调用此属性
-提供legs属性赋值的setLegs()方法,在此方法中加入legs属性赋值的判断逻辑if(legs >= 0 && legs % 2 == 0)
将此方法暴露出去,使得在Animal类的外部调用此方法,对legs赋值
-提供legs属性获取的getLegs()方法,此方法对外暴露,使得在Animal类的外部还可以调用此属性的值
2.4 4种权限具体使用
-类:只能使用public、缺省修饰
-类的内部成员:可以使用4种修饰符
2.5 开发中4种权限修饰符的使用情况
比较高:public、private
比较低:缺省、protected
3 封装性的体现
-场景一:私有化(private)类的属性,提供公共(public)的get和set方法,对此属性进行获取或修改
-场景二:将类中不需要对外暴露的方法,设置private
-场景三:单例模式中构造器private,避免在类的外部创建实例
this关键字的使用
1.目前可能出现的问题?以及解决方案?
当声明一个属性对应的SetXxx方法时,通过形参给对应的属性赋值。如果形参名和属性名同名,如何在方法中区分变量?
解决方案:使用this。使用this修饰的变量,表示属性,没有this修饰的变量,表示形参
2.this可以调用的结构:成员变量、方法、构造器
3.this的理解:当前对象(方法调用时)或当前正在创建的对象(在构造器调用时)
4.1 this调用属性和方法
针对于方法内的使用情况(非static修饰的方法):
一般情况:我们通过对象a调用方法,可以在方法内调用当前对象a的属性或其他方法。此时,我们可以在属性和其他方法前使用“this.”
继承性
1.继承性的理解
-生活上:财产、血缘的继承
-代码层面
-自上而下:定义一个A类,在定义另一个B类时,发现B类的功能与A类相似,考虑B类继承于A类
-自下而上:定义了类B、C、C等,发现B、C、D有类似的属性和方法,则可以考虑将相同的属性和方法进行抽取,
封装到A类中,让类B、C、D继承于类A,同时类B、C、D中的相似的功能就可以删除了
2.继承性的好处
-继承的出现减少了代码的冗余,提高了代码的复用性
-有利于功能的扩展
-继承的出现让类与类之间产生了‘is-a’的关系,为多态提供了前提
-继承描述事物之间的所属关系‘is-a',父类更通用、一般,子类更具体
3.继承性的格式
class A{
}
class B extends A{
}
4 继承中的基本概念
-类A:父类、supperClass、超类、基类
-类b:子类,subClass、派生类
5.有了继承性以后
-子类获取父类中声明的所有属性和方法
-由于封装性的影响,可能子类不能直接调用父类中声明的属性或方法
-子类在继承父类以后,还可以扩展自己特有的功能(体现:增加特有的属性、方法)
extends:延展、扩展,延伸
-继承之前,判断一下是否有is a的关系
-子类和父类,要区分集合和子集
6 默认的父类
java中声明的类,如果没有显示的声明其父类时,则默认继承于java.lang.Object
6补充说明
-java支持多层继承
-直接父类、间接父类
-java中的子类的概念是相对的
-java中的一个父类可以声明多个子类,一个子类只有一个父类(java的单继承性)
一、super关键字的使用
抽象类与接口
. abstract的概念:抽象的
2. abstract可以用来修饰:类方法
3. 具体使用:
3.1 abstract修饰类
-此类称为抽象类
-抽象类不能实例化
-抽象类包含构造器,子类对象实例化,需要直接或间接的调用到父类构造器
-抽象类中可以没有抽象方法,有抽象方法所在的类,一定是抽象类
3.2 abstract修饰方法
-此方法称为抽象方法
-抽象方法只有方法的声明,没有方法体
-抽象方法其功能是确定的(方法声明确定),只是不知道如何实现(体现没有方法体)
-子类必须重写父类中的所有抽象方法之后,才可以实例化。否则,子类依然是一个抽象类
4. abstract不能使用的场景:
4.1 abstract不能修饰哪些结构?
属性、构造器、代码块等。
4.2 abstract 不能与哪些关键字共用?(自洽)
私有方法、静态方法、final的方法、final类。
-私有方法不能重写
-避免静态方法使用类进行调用
-final的方法不能被重写
-final修饰的子类不能有子类
接口的使用
1. 接口的理解:接口的本质是契约、标准、规范
2. 定义接口的关键字:interface
3. 接口内部结构的说明:
-可以声明:
属性:必须使用public static final修饰(全局变量)
方法:jdk8之前:只能声明抽象方法,修饰为public static
jdk8:声明静态方法、默认方法
jdk9:声明私有方法
-不可以声明:构造器、代码块等
类的多态
面向对象特征之三:多态性
1.如何理解多态性?
理解:一个事物的多种形态
2.java中多态性的体现:
子类对象的多态性:父类的引用指向子类的对象
3.多态性的应用:
虚拟方法调用
在多态的场景下,调用方法时
编译时,认为方法是左边声明的父类的类型的方法(即被重写的方法)
执行时,实际执行的是子类重写父类的方法
简称:编译看左边,运行看右边
子类需要继承父类的属性、方法
如何调用?使用super关键字
2.super的理解:父类的
3.super可以调用的结构:属性、方法、构造器
3.1 super调用属性、方法
-子类继承父类以后,我们就可以在子类的方法或结构中,调用父类中声明的属性或方法(满足封装性的前提下)
-可使用“super.”的结构,表示调用父类的属性或方法
-一般情况下,我们可以考虑省略“super.”的结构,但是,如果出现子类重写父类的方法或子父类中出现相同的属性或方法,
必须使用“super.”声明,显示调用父类被重写的方法或父类中声明的同名的属性
3.2 super调用构造器
①子类继承父类时,不会继承父类的构造器。只能通过“super.(形参列表)”,调用父类指定的构造器
②规定:“super(形参列表)”,必须声明在构造器的首行
③在构造器的首行可以使用“this(形参列表)”,调用本类中重载的构造器
在构造器的首行,“this(形参列表)”和“super(形参列表)”只能二选一
④如果子类构造器的首行既没有调用“this(形参列表)”也没有调用“super(形参列表)”,
则子类构造器默认调用“super(形参列表)”,即调用父类中空参的构造器
⑤子类的任何一个构造器,要么调用本类中重载的构造器,要么调用父类的构造器
⑥一个类声明n个构造器,最多有n-1个构造器中使用“this(形参列表)”
剩下那个一定使用“super(形参列表)”
--> 我们在通过子类的构造器创建对象时,一定在调用子类构造器的过程中,直接或间接调用到父类的构造器
调用父类构造器,才能将父类中声明的属性或方法加载到内存中,供子类对象使用
二、子类对象实例化过程
class Creature{//生物类
//属性、方法、构造器
}
class Animal extends Creature{//动物类
}
class Dog extends Animal{//狗类
}
class DogTest{
public static void main(String args){
Dog dog = new Dog();
}
}
1.从结果角度来看:类的继承性
创建对象后,子类对象就获取了其父类中声明的所有属性和方法,在权限允许的情况下可以直接调用
2.从过程角度来看:
当通过子类的构造器创建对象时,子类的构造器一定会直接或者间接的调用到其父类的构造器,父类也会一层一层往父类调用,
直到调用默认父类Object中的构造器为止
3.创建子类对象的过程中,内存中到底有几个对象
只有一个对象!即为当前new后面构造器对应的类的对象
类的高级特性
static关键字的使用
1. static:静态的
声明变量和方法时,加上static修饰,称为类变量和类方法(或静态变量和静态方法),实现成员和实例之间的共享。
2. static用来修饰的结构:属性、方法、代码块、内部类
3. static修饰属性
3.1 复习:变量的分类
方式1:按照数据类型:基本、引用
方式2:按照类中声明的位置:
成员变量:按照是否使用static修饰进行分类
使用static修饰的成员变量:静态变量、类变量
不使用static修饰的成员变量,非静态变量、实例变量
局部变量:方法内、方法形参、构造器内、构造器形参、代码块内等
3.2 静态变量:类中的属性使用static进行修饰
对比静态变量和实例变量:
① 个数
-静态变量:在内存空间中只有一份,被类的多个对象共享
-实例变量:类的每一个实例(或对象)都保存一份实例变量
② 内存位置
-静态变量:jdk6及之前存在方法区(叫做永久代,jdk8及以后叫做元空间),jdk及以后放在堆空间
-实例变量:存放在堆空间的对象实体中
③ 加载时机
-静态变量:随着类的加载而加载,由于类只会加载一次,所以静态变量只有一份
-实例变量:随着对象的创建而加载。每个对象拥有一份实例变量
④ 调用者
-静态变量:可以被类和对象调用
-实例变量:只能被对象调用
⑤ 判断是否可以调用 ---> 从生命周期的角度解释
类变量 实例变量
类 √ ×
对象 √ √
⑥ 消亡时机
-静态变量:随着类的卸载而消亡
-实例变量:随着对象的消亡而消亡
4. static修饰方法:(类方法、静态方法)
-随着类的加载而加载
-可以通过“类.静态方法”的方式直接调用静态方法
-静态方法内可以调用静态的属性或静态的方法
不能调用非静态的结构(比如:属性、方法)
-非静态方法可以调用当前类中的静态结构(属性、方法)或非静态结构(属性、方法)
-static修饰方法内不能使用this和super
类方法 实例方法
类 √ ×
对象 √ √
final关键字的使用
1. final的理解:最终的
2. final可以用来修饰的结构:类、方法、变量
3. 具体说明:
3.1 final修饰类:此类表示不能被继承
比如:String、StringBuffer、StringBuilder类
3.2 final修饰方法:表示方法不能被重写
比如:Object类中的getClass()
3.3 final修饰变量:即可以修饰成员变量,也可以修饰局部变量
此时的“变量”变成了“常量”,意味着一旦赋值,就不可以更改
3.3.1 final修饰成员变量:有哪些位置可以给成员变量赋值?
-显示赋值
-代码块中赋值
-构造器中赋值
3.3.2 final修饰局部变量:一旦赋值就不能修改
-方法内声明的局部变量:在调用局部变量前,一定需要赋值。而且一旦赋值,就不可更改
-方法的形参:在调用此方法时,给形参进行赋值。而且一旦赋值,就不可更改
4. final与static的搭配:修饰成员变量时,次变量称为:全局变量
比如:Math.PI
类的成员之四:代码块
回顾:类中可以声明的结构:属性、方法、构造器、代码块(或初始化块)、内部类
1. 代码块(或初始化块)的作用
用来初始化类或对象的信息(即初始化类或对象的成员变量)
2. 代码块的修饰:
只能使用static
3. 代码块的分类:
静态代码块:使用static
非静态代码块:未使用static
4. 具体使用
4.1 静态代码块:
-随着类的加载而调用
-类加载执行一次,静态代码块也执行一次
-作用:初始化类的信息
-内部可以声明变量、调用属性或方法、编写输出语句等操作。
-静态代码块执行先于非静态代码块
-如果声明多个静态代码块,则按照声明的先后顺序执行
-静态代码块内部只能调用静态的结构(即静态的属性、方法),不能调用非静态的结构(即非静态属性、方法)
4.2 非静态代码块:
-随着对象的创建而执行
-每创建当前类的实例,就会执行一次非静态代码块
-作用:初始化对象的信息
-内部可以声明变量、调用属性或方法、编写输出语句等操作。
-如果声明多个非静态代码块,则按照声明的先后顺序执行
-静态代码块内部可以调用静态的结构(即静态的属性、方法),也可以调用非静态的结构(即非静态属性、方法)
类的成员之五:内部类
1. 什么是内部类?
将一个类A定义在另一个类B里面,里面的那个类A叫做内部类(InnerClass),外面的类B叫做外部类(OuterClass)
2. 为什么需要内部类?
当一个类只服务于另一个类时,推荐定义内部类,遵循高内聚、低耦合的面向对象开发原则
3. 内部类的使用举例
Thread类内部声明了State类,表示线程的生命周期
HashMap类中声明了Node类,表示封装的Key和Value
4. 内部类的分类:
-成员变量:直接声明在外部类的里面
-使用static修饰:静态的成员内部
异常
异常处理概述
1. 什么是异常
指的是程序在执行过程中,出现的非正常情况,如果不处理最终会导致JVM的非正常停止。
2. 异常抛出机制
java中把不同的异常用不同的类表示,一旦发生某种异常,就创建该异常类型的对象,并且抛出(throw)。然后程序员可以捕获(catch)到这个异常对象,并处理;
如果没有捕获(catch)这个异常对象,那么这个异常对象将会导致程序终止。
3. 如何对待异常
对于程序出现的异常,一般有两种解决方法:一是遇到错误就终止程序的运行。另一种方式是程序员在编写程序时,
就充分考虑到各种可能发生的异常和错误,极力预防和避免。实在无法避免的,要编写相应的代码进行异常检测,
以及‘异常的处理’,保证代码的‘健壮性’。
4. 异常的体系结构
java.lang.Throwable:异常体系的根父类
-java.lang.Error:错误。java虚拟机无法解决的严重问题。如:JVM系统内部错误、资源耗尽等严重情况
StackOverFlowerError、OutOfMemoryError
-java.lang.Exception:异常。我们可以编写针对性的代码进行处理
编译时异常:(受检异常)在执行javac.exe命令时,出现的异常。
-ClassNotFoundException
-FileNotFoundException
-IOException
运行时异常:(非受检异常)在执行java.exe命令时,出现的异常。
-ArrayIndexOutOfBoundsException
-NullPointerException
-ClassCastException
-NumberFormatException
-InputMismatchException
-ArithmeticException
1. 方式一(抓抛模型):try-catch-finally
过程1:“抛”
程序在执行的过程中,一旦出现异常,就会在出现异常的代码处,生成对应异常类的对象,并将此对象抛出
一旦抛出,此程序就不在执行其它的代码了
过程2:“抓”
针对于过程1中抛出的异常对象,进行捕获处理。此捕获处理的过程,就称为抓
一旦将异常进行了处理,代码就可以继续执行
2. 基本结构:
try{
...... //可能产生异常的代码
}
catch(异常类型一 e){
...... //当产生异常类型1型异常时的处置措施
}
catch(异常类型二 e){
...... //当产生异常类型2型异常时的处置措施
}
finally{
...... //无论是否发生异常,都无条件执行的语句
}
3. 使用细节:
-将可能出现异常的代码声明在try语句中。一旦代码出现异常,就会自动生成一个对应异常类的对象。并将此对象抛出。
-针对于try中抛出的异常类的对象,使用之后的catch语句进行匹配。一旦匹配上,就进入catch语句块进行处理。
一旦处理接触,代码就可以继续执行下去
-如果声明了多个catch结构,不同的异常类型在不存在子父类关系的情况下,声明没有顺序要求
如果多个异常类型满足子父类的关系,则必须将子类声明在父类结构的上面。否则,报错。
-catch中异常处理的方式:
①自己编写输出的语句。
②printStackTrace():打印异常的详细信息。(推荐)
③getMessage():获取发生异常的原因
-try catch可以嵌套使用
4. 开发体会:
-对于运行时异常:
开发中,通常就不进行显示的处理了
一旦在程序执行中,出现了运行时异常,那么就根据异常的提示信息修改代码即可
-对于编译时异常:
一定要处理。否则编译不通过。
5. finally的使用说明:
5.1 finally的理解
-将一定要被执行的代码声明在finally结构中
-无论在try中或catch中是否存在任未被处理的异常,无论try中或catch中是否存在return语句等
finally中声明的语句都一定要被执行
-finally语句和catch语句是可选的,但finally不能单独使用
5.2 什么样的代码我们一定要声明在finally中呢?
-开发中一些资源(比如:输入流、输出流、数据库连接、Socket连接等资源),在使用完以后,必须显式的进行关闭操作,
否则,GC不会自动的回收这些资源。进而导致内存的泄漏
为了保证这些资源在使用完以后,不管是否出现了未被处理的异常情况下,这些资源能被关闭。我们必须将这些操作声明在finally中
异常处理的方式2:throws
1. 格式:在方法的声明出使用“throws 异常类型1,异常类型2,...”
2. 举例:
public void test() throws 异常类型1,异常类型2,...{
//可能存在编译时异常
}
3. 是否真正处理了异常?
-从编译是否通过的角度看,看成是给出了异常,万一要是出现时候的解决方案。此方案就是,继续往上抛(throws)
-但是,此throws的方式,仅是将可能出现的异常抛给了此方法的调用者。此调用者任然需要考虑如何处理相关异常
从这个角度来看,throws的方式不算是真正意义上处理了异常。
4. 方法重写的要求:(针对于编译异常来说)
常用类
1.String类的理解(以jdk8为例说明)
1.1 类的声明
public final class String
implements java.io.Serializable, Comparable<String>, CharSequence
-final:String是不可被继承的
-Serializable:可序列化接口。凡是实现此接口的类的对象都可以通过网络或本地流进行数据的传输。
-Comparable:凡是实现此接口的类,其对象都可以比较大小。
1.2 内部声明的属性
jdk8中:
private final char value[];//储存字符串数据的容器
-final:指明此value数组一旦初始化,其地址就不可变
jdk9开始:为了节省内存空间
private final byte[] value;
2. 字符串常量的储存位置
-字符串常量都储存在字符串常量池(StringTable)中
-字符串常量池不允许存放两个相同的字符串常量
-字符串常量池,在不同的jdk版本中,存放位置不同
jdk7之前,字符串常量池存放在方法区
jdk7及之后,字符串常量池,存放在堆空间
3. String的不可变性的理解
4. String实例化的两种方式
【面试题】
String s2 = new String("hello");在内存中创建了几个对象?
一个是堆空间中new的对象。另一个是在字符串常量池中生成的字面量
5. String的连接操作:+
情况1:常量 + 常量 :结果仍然储存在字符串常量池中,返回此字面量的地址。注:此时的常量可能是字面量,也可能是final修饰的常量
情况2:常量 + 变量 或 变量 + 常量 :都会通过new的方式创建一个新的字符串,返回堆空间中此字符串对象的地址值
情况3:调用字符串的intern() :返回的是字符串常量池中字面量的地址
(了解)情况4:concat():无论是常量调用方法,还是变量调用方法,无论参数是参量还是变量,调用完concat()方法都会返回一个新new的对象
6. String的构造器和常用方法
6.1 构造器
public String():初始化新创建的String对象,以使其表示空字符序列
public String(String original):初始化一个新创建的String对象,使其表示一个与参数相同的字符序列
public String(char value[]):通过当前参数中的字符数组来构造新的String
public String(char value[], int offset, int count):通过字符数组的一部分来构造新的String
public String(byte bytes[]):通过使用平台的默认字符集解码当前参数中的字节数组来构造新的String
public String(byte bytes[], String charsetName):通过使用指定的字符集解码当前参数中的字节数组来构造新的String
针对于StringBuilder来说:
内部属性有:
char[] value;//储存字符序列
int count;//实际储存的字符的个数
StringBuilder sb1 = new StringBuilder();//char[] value = new char[16];
StringBuilder sb1 = new StringBuilder("abc");//char[] value = new char[16+"abc".length()];
sb1.append("ac");//value[0] = 'a';value[1] = 'c';
sb1.append("b");//value[2] = 'b';
当count超过value.length()时,就需要扩容:默认扩容为原容量的2倍+2,并将原有value数组中的元素复制到新的数组中
3. 原码启示
-如果开发中需要频繁的针对于字符串进行增、删、改、查等,建议使用StringBuilder或StringBuffer替换String,因为String效率低
-如果开发中,不涉及 到线程安全问题,建议使用StringBuilder替换StringBuffer。因为StringBuilder效率高
-如果开发中大体确定要操作的字符的个数,建议使用带int capacity参数的构造器,避免底层多次扩容操作,性能降低
4. StringBuffer和StringBuilder中的常用方法
增:
StringBuffer append(xx):提供了很多的append()方法,用于进行字符串追加的方式拼接
删:
StringBuffer delete(int start,int end):删除[start,end]之间的字符
StringBuffer deleteCharAt(int index):删除[index]位置的字符
改:
StringBuffer replace(int start,int end,String str):替换[start,end)范围的字符序列
void setCharAt(int index,char c):替换[index]位置字符
查:
char charAt(int index):查找指定位置index位置上的字符
插:
StringBuffer insert(int index,xx):在[index]位置上插入xx
长度:
int length():返回储存的字符数据的长度
反转:
StringBuffer reverse():反转
5. 对比三者的执行效率
效率从高到低排列:
StringBuilder > StringBuffer > String
/*
注意:Date 日期 和 Data 数据 数据库:Database
Java中针对时间日期类型提供了Date工具类。
使用之前需要进行导包,和Scanner类非常类似: java.util.Date
注意是util下的Date!
导包:大部分的常用包都没有导入 ,原因:Java自带的一个包叫做 Java.lang.*包 该包下得类都是自动导入
例如String System
需要的手动导入:Scanner
*/
/*
常见的获取Date对象方式如下:
Date d = new Date();//获取当前时间
常见的过时方法:···
*/
Date d = new Date();//下面的方法是过时的,但是这个对象不过时
System.out.println(d);//发现:结果是对中国人不友好的
//所以需要通过大量的过时方法来对中国人民友好
//过时方法:仅仅是针对 大量的用户 翻译:如果你的项目很多很多人使用,就不推荐使用下面的方法
//如果用户数量较少,可以使用,但是不推荐。
// int year = d.getYear();
// System.out.println(year+1900);
// int month = d.getMonth();
// System.out.println(month + 1);
// int date = d.getDate();
// System.out.println(date);
// int h = d.getHours();
// int m = d.getMinutes();
// int s = d.getSeconds();
// System.out.println(h+":" + m + ":" + s
/*
由于Date对象的99%的方法都已经过时
备注:Date d = new Date();还是没有过时,依然是获取当前时间的唯一方法。
所以Java提供了SimpleDateFormat类,来替代Date的绝大部分方法,来对时间进行操作。
Simple:简单的
Date:日期
Format:格式
语法如下:
SimpleDateFormat sdf = new SimpleDateFormat(“日期的具体格式”);
日期具体格式如下:
yyyy-MM-dd HH:mm:ss
yyyy/MM/dd HH:mm:ss
yyyy-MM-dd
HH:mm:ss
格式可以随便改,但是字母不能。yyyy表示年 MM表示月 dd日期 HH小时 mm分钟 ss秒
注意:yyyy和MM 和dd和mm和ss 是固定的
但是HH表示24小时制的时间
hh表示12小时的时间
其中字母定死了,符号可以根据场景改变。
字母可以省略,例如只保留年月日或只保留时分秒。
*/
// //如何获取当前时间
// //1.定义时间的格式
// SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
// //2.获取当前时间
// Date d = new Date();
// //3.格式化时间
// String str = sdf.format(d);
// System.out.println(str);
//某些场景:可能需要过去或未来的时间 方法如下:
String str = "2030/8/8 12:12:12";
SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
Date date = sdf.parse(str);
System.out.println(date);
/*
Java提供了Math类来执行基本的数学运算。
该类处于java.lang下,所以使用无需导包。
该类下的所有方法都是静态方法,故无需new对象。
*/
public static void main(String[] args) {
//floor 地板
//ceil 天花板
// doule Math.floor(double x); 向下取整,但是依然是double类型。
System.out.println(Math.floor(1.1));//1.0
System.out.println(Math.floor(1.0));//1.0
// double Math.ceil(double x);向上取整,但是依然是double类型
System.out.println(Math.ceil(1.1));//2.0
System.out.println(Math.ceil(1.0));//1.0
// long Math.round();四舍五入
System.out.println(Math.round(1.1));//1
System.out.println(Math.round(1.5));//2
System.out.println(Math.round(1.9));//2
//总结:四舍五入如果碰到± xxx.5 优先往大的数走
System.out.println(Math.round(-1.1));//-1
System.out.println(Math.round(-1.5));//-1
System.out.println(Math.round(-1.9));//-2
// double Math.random();取一个大于等于0但是小于1的小数 官方概念是这样,但是实际上可以不用考虑0
System.out.println(Math.random());
}
}
/*
学会看API(教学文档,可以了解一些方法的组成以及如何使用):语法如下:
数据类型 方法名(参数列表);
数据类型: 方法的返回值
/*
包装类是专门针对于基本数据类型而言。
由于基本数据类型没有方法或属性。
为了更加方便对基本数据类型的值进行操作,Java提供了八大基本数据类型的包装类。
boolean byte
char short
int float
double long
Boolean Byte
Character Short
Integer Float
Double Long
*/
//包装类如何声明变量 和String一样
Integer a = 123;//直接声明
Integer b = new Integer(123);//通过构造方法声明
//包装类的用途:
//1.作为实体类属性的数据类型,替换基本数据类型。
//以前的写法 见下方的Student类
Student s = new Student(123, "张三", 18,18);
Student s1 = new Student();
System.out.println(s);
System.out.println(s1);
//缺点:基本数据类型的默认值为0 而引用数据类型的默认值为null
//2.将字符串转为对应的基本数据类型
String str = "123445";
int i = Integer.parseInt(str);
System.out.println(i);
double d = Double.parseDouble(str);
/*
包装类的补充:
仅做了解为了面试 实际开发用不到
*/
public static void main(String[] args) {
//自动装箱值得是:将基本数据类型 变成 包装类
// int a = 11;
// Integer b = a;//此过程就是自动装箱
//
// //自动拆箱值得是:将包装类转成基本数据类型
// Integer c = 1;
// int d = c.intValue();
//
//常见面试题:
// String str1 = "你好";
// String str2 = "你好";
// String str3 = new String("你好");
// System.out.println(str1==str2);//true
// System.out.println(str1==str3);//f
//
// System.out.println(str1.equals(str2));//t
// System.out.println(str1.equals(str3));//t
//
// System.out.println(str2==str3);//f
// System.out.println(str2.equals(str3));//t
//Integer和String的类似度高达99%
// Integer str1 = 1;
// Integer str2 = 1;
// Integer str3 = new Integer(1);
// System.out.println(str1==str2);//true
// System.out.println(str1==str3);//f
//
// System.out.println(str1.equals(str2));//t
// System.out.println(str1.equals(str3));//t
//
// System.out.println(str2==str3);//f
// System.out.println(str2.equals(str3));//t
Integer str1 = 1111;
Integer str2 = 1111;
Integer str3 = new Integer(1111);
System.out.println(str1==str2);//false
System.out.println(str1==str3);//f
System.out.println(str1.equals(str2));//t
System.out.println(str1.equals(str3));//t
System.out.println(str2==str3);//f
System.out.println(str2.equals(str3));//t
//Integer直接声明变量和String非常类似 ,都有一个类似缓存池的存在
//String直接声明:先去缓存池找是否存在,如果不存在就new一个,如果存在就直接引用
//Integer直接声明:现在-128~127的缓存池找是否存在,如果不存在就new一个,如果存在就
什么是对象:
对象就是类的实例化
什么是实例化:将类进行new操作。
例如: 类名 变量名 = new 类名();
Test t = new Test();
称之为:t为Test类的实例化对象。
实例化之后的对象可以:对类的属性或方法进行访问调用。
工具类的作用
创建一个工具类,该类的功能如下:
提供方法a1(),该方法只需要完成输出一句话即可。
提供方法a2(), 该方法只需要完成计算方法传递来的2个int值之和并返回。
该工具类写好之后的意义:方便后续去调用它。
*/
public class Test2 {//这个类是工具类
public void a1() {
System.out.println("我是工具类的a1方法");
}
public int a2(int a , int b ) {
return a+b;
}
}
实体类
创建一个实体类,该类的功能如下:
提供3个属性,分别是name,age,sex。
提供一个方法,该方法只需要打印这三个属性值即可。
*/
public class Test3 {//假设是一个实体类
String name;
int age ;
char sex;
//该方法的作用:输出这个类的三个属性
public String out() {
return "name=" + name +",age=" + age +",sex=" + sex;
}
}
测试类
课堂练习3:
何为测试类:暂时这么理解:有main方法的类
创建一个测试类,创建三个对象分别赋值,并将对象的值打印在控制台上。
*/
public class Test4 {
public static void main(String[] args) {
// 创建一个测试类,调用刚刚创建的工具类的2个方法。
// 如何调用?
// 1.要想调用某个类的方法,需要实例化对象。
// Test2 t2 = new Test2();
// t2.a1();
// int sum = t2.a2(10, 30);
// System.out.println(sum);
//创建实体类的三个对象分别赋值,并将对象的值打印在控制台上。
Test3 t1 = new Test3();
t1.name = "张三";
t1.age = 18;
t1.sex = '男';
Test3 t2 = new Test3();
t2.name = "李四";
t2.age = 20;
t2.sex = '女';
System.out.println(t1.out());
System.out.println(t2.out());
}
}
何为测试类
课堂练习3:
何为测试类:暂时这么理解:有main方法的类
创建一个测试类,创建三个对象分别赋值,并将对象的值打印在控制台上。
*/
public class Test4 {
public static void main(String[] args) {
// 创建一个测试类,调用刚刚创建的工具类的2个方法。
// 如何调用?
// 1.要想调用某个类的方法,需要实例化对象。
// Test2 t2 = new Test2();
// t2.a1();
// int sum = t2.a2(10, 30);
// System.out.println(sum);
//创建实体类的三个对象分别赋值,并将对象的值打印在控制台上。
Test3 t1 = new Test3();
t1.name = "张三";
t1.age = 18;
t1.sex = '男';
Test3 t2 = new Test3();
t2.name = "李四";
t2.age = 20;
t2.sex = '女';
System.out.println(t1.out());
System.out.println(t2.out());
}
}
包
为什么要将以上代码分开写?而不写在同一个类里。
1.方便管理阅读,如果代码量一多起来,放在一个类里面更加困难进行阅读。
2.各司其职 每个类都有每个类的作用。
3.例如:我们现在所学的 测试类(有main方法的就叫测试类)
那么测试类的规范:只需要执行代码。不需要过程代码。
例如只需要调用方法,不需要操心方法内部是什么。
一般而言测试类都是放在com.yyzy.test包下
回顾:包package的作用:将代码进行分类。
常见的包:com.yyzy.test包
今日所学:工具包和工具类 简称util
何为工具包:存放工具类的包
何为工具类:可以被复用的方法。
例如将密码进行加密。 将所有的大写字母变成小写字母
计算N个数之和。 将数组进行排序。等等
思考:登录方法是不是工具类?
答:不是,登录是业务类。
今日所学:实体类和实体包 简称entity
何为实体包:存放实体类的包
何为实体类:世间万物抽象的存在,例如一个人,一个猫,一张银行卡
等拥有很多属性的类。例如淘宝上的所有商品,例如微信的所有登录用户。
*/
成员变量和局部变量
/*
成员变量和局部变量
成员变量又称为全局变量:声明在方法之外的,在该类中的任何地方都可以访问
特点:如果没有赋予初始值,会自动赋予初始值
boolean类型默认值:false (false其实就是0,true其实就是1)
数字类型: byte short int long double float 默认值都是0或0.0
char : " " 是一个空格
任何引用数据类型的默认值都是null 例如String 数组
局部变量:声明在方法之内的,只能在声明的方法内部访问。
特点:如果没有赋予初始值,不会自动赋予初始值,所以必须要赋值。
*/
//以下五个变量都是成员变量 其中1个有初始值 另外4个没有初始值
byte a = 1;
int b;
boolean c;
String d;
double e;
float aa;
char bb;
public static void main(String[] args) {
long f = 11;//局部变量必须要赋予初始值
//这里要注意:由于没有学习static的甩法,所以暂时不在main方法中演示
//如何证明没有赋值的成员变量的默认值为多少?
//1.调用a方法 2.实例化a方法所在的类即可
Test6 t = new Test6();
t.a();
}
public void a() {
System.out.println(d);//在任何地方都可以访问成员变量。不管有没有赋值
}
需要的方法
Java为什么需要方法:
将一些重复的代码放在一个方法内部,实现重复的调用,简化代码
方法的理解:帮助我们去做某一件具体的事。例如计算求和。例如登录功能。例如注册功能。
方法1的组成语法:调用这个方法,不需要得到什么返回值。
public void 方法名(参数列表){ //其中参数列表: 数据类型 变量名 , 数据类型 变量名 的格式
代码体
}
方法2的组成语法:调用这个方法我需要得到什么类型的返回值
public 返回数据类型 方法名(参数列表){ //其中参数列表: 数据类型 变量名 , 数据类型 变量名 的格式
代码体;
return 返回值;
}
*/
方法的声明语句
方法的声明语法
方法是为了完成一个功能而组合在一起的语句块
方法的声明由方法名、参数、返回值类型及函数体组成。声明如下:
修饰符 返回值类型 方法名(参数列表)[throws 异常列表]{
//方法体
}
方法头:定义方法的访问特性(如:public)、使用特性(如:static)、返回值类型、方法名称、参数和抛出异常等
方法的返回值
通过return语句将方法的结果返回给调用者,return语句返回的值类型和函数头中声明的返回值类型要一致,void类型没有返回值
方法的参数
方法头中的变量称形式参数简称形参,方法可以不包含参数,没有参数的方法称为无参方法
2、方法的调用
方法名(实际参数值); 无参方法直接调用,不用传参
方法的参数传递
如果参数是基本数据类型,实参单元和形参单元存储的均为数据本身。参数传递就是将实参单元的数据复制给形参单元,在方法内修改形参的值,不影响实参
如果参数是数组或对象,则参数单元存放的是引用地址,也就是将实参单元存放的地址复制给形参单元,实参和形参将指向同一数据或对象。对形参数组或对象的操作访问,实际上是对实参数组或对象的操作访问。因此,在方法内修改参数的内容将影响实参
3、递归调用
递归就是方法调用自身,这个过程称为递归,调用自己的方法称为递归方法
递归的两个基本要素
边界条件,显示确定递归到何时终止,也称为递归出口
递归模式,显示大问题是如何分解为小问题,也称为递归体
Object
equals
Object类是Java中所有类的父类,所以Object下的方法同时也是所有类的共有方法, 即使是你自定义的一个类,也拥有Object的方法。 所以需要掌握Object类下的常见方法: 1.equals方法 a.equals(b);//其中a和b都要是一个对象而非一个基本数据类型或一个类。 含义:判断两个对象是否为同一个对象(内存地址是否相等,本质就是==),返回一个boolean值。 凡是new出来的不可能是同一个对象。
public static void main(String[] args) {
Student s1 = new Student(1, "张三", 12);
Student s2 = new Student(1, "张三", 12);
boolean b = s1.equals(s2);
System.out.println(b);
//注意上述s1和s2即使每个属性值都一样,但是依然返回false,原因就是都是new出来的。
//注意:如果需要在某些特定场景,equals不是判断内存地址,而是判断id如何实现。
常见面试题
答案1:==如果作用于基本数据类型。就判断值是否相等。
答案2:==如果作用于引用数据类型,就判断地址是否相等
String str1 = new String("你好") ;
String str2 = new String("你好") ;
System.out.println( str1 == str2);//false
System.out.println( 1 == 1);//true
String str1 = "你好";
String str2 = "你好";
System.out.println( str1 == str2);true
意味着str1和str2的地址是一样的。
总结:String声明变量的区别:
String str = new String("xx");
不管xx是什么,都要重新开辟一个新的内存。
String str = xx ;
如果xx存在了就不去开辟了 而是直接引用已经存在的,
答案3:equals:只能作用于引用数据类型,本质就是==
答案3:但是大部分的类都重写了equasl方法,例如String ,Date
答案3:重写之后的equals则是判断值是否相等
String str1 = "你好";
String str2 = "你好";
System.out.println( str1.equals(str2) );//true 错误的解释:因为str1和str2都是直接声明的所有地址相等
String str1 = new String("你好") ;
String str2 = "你好";
System.out.println( str1.equals(str2) );//true 原因:String 也重写了equals方法,是判断值是否相等
而非地址
to String
总结0:直接打印对象,和打印对象的.toString方法 效果是一样的 一般都是省略 总结1:Object的toString打印:对象的内存地址,格式: 路径+ @ + 内存地址 如果存在某个场合,希望打印的是具体属性信息而非地址,则可以通过重写toString方法实现。 大部分的实体类都会重写toString,原因:方便阅读实体对象是否符合要求
public class Demo3 { public static void main(String[] args) { Student s = new Student(1, "张三", 18); System.out.println(s.toString()); System.out.println(s); } }
数组
一维数组
1、数组的概念(Arrays)
概念:多个相同类型数据按一定数据按一定顺序排列的组合,并使用一个名字命名,并通过编号的方式对这些数据进行统一管理
简称:多个数据组合
java中的容器:数组、集合框架:在内存中对多个数据储存
2、几个相关概念
>数组名
>数组的元素(即内部储存的多个元素)
>数组的下标、角标、下角标、索引、index(即找到指定数组元素所使用的编号)
>数组长度(即数组容器中储存的元素个数)3、数组的特点:
>数组中的元素在内存中是依次紧密排列的、有序的。
>数组属于引用数据类型的变量,数组元素既可以是基本数据类型也可以是引用数据类型
>数组,一旦初始化完成,其长度不可改变
>创建数组对象会在内存中开辟一整块“连续的空间”。占据的空间的大小,取决于数组的长度和数组中元素的类型。复习:变量按照数据类型分类
4.1 基本数据类型:byte、short、int、long、float、double、char、Boolean
4.2 引用数据类型:类、数组、接口、枚举、注解、记录
5、数组的分类
5.1 按照元素的类型:基本数据类型元素的数组,引用数据类型元素的数组
5.2 按照数组的维数来分:一维数组、二维数组
6、一维数组的使用(6个基本点)
>数组的声明和初始化
>调用数组的指定元素
>数组的属性:length,表示数组的长度
>数组的遍历
>数组的默认初始化值
>一维数组的内存解析7、数组元素的默认初始化值的情况
注意:以数组的动态初始化方式为例
>整数型数组元素的默认初始化值:0
>浮点型数组元素的默认初始化值:0.0
>字符型数组元素的默认初始化值:0或'\u0000'
>布尔型数组元素的默认初始化值:false
>引用型数组元素的默认初始化值:null8、一维数组的内存解析
8.1 java中的内存结构是如何划分的?(主要关心JVM运行时的内存环境)
>将内存区域划分为5个部分:程序计数器、虚拟机栈、本地方法栈、堆、方法区
>与目前数组相关的结构:比如:int[] arr = new int[]{1,2,3};
>虚拟机栈:用于存放方法中声明的变量。比如:arr
>堆:用于存放数组的实体(即数组中的所有元素)。比如:1,2,3栈(stack) 堆(heap)
基本使用//1.数组的声明和初始化
//复习:变量的定义格式:数据类型 变量名 = 变量值;
int num1= 10;
//声明数组
double[] prices;
//数组的初始化
//静态初始化:数组变量的赋值与数组元素的赋值操作同时进行
prices = new double[]{20.32,43.21,43.22};
//数组声明和初始化
//动态初始化:数组变量的赋值与数组元素的赋值操作分开进行
String[] foods = new String[4];
//其它正确方式
int arr[] = new int[]{1,2,3,4};
//类型推断
int arr1[] = {1,2,3};
//2.数组元素的调用
//通过角标的方式,获取数组的元素
//角标范围从0到数组长度-1
System.out.println(prices[0]);
System.out.println(prices[2]);
//System.out.println(prices[3]);数组越界:ArrayIndexOutOfBoundsException
foods[0] = "拌海蜇";
foods[1] = "龙须菜";
foods[2] = "炝冬笋";
foods[3] = "玉兰片";
//3.数组长度:用来描述数组容器中容量的大小
//使用length属性表示
System.out.println(prices.length);
System.out.println(foods.length);
//4.如何遍历数组
for (int i = 0; i < foods.length-1; i++) {
System.out.println(foods[i]);
}
二维数组
1、二维数组的理解
- 对于二维数组的理解,可以看成一个一维数组又作为另一个一位数组的元素存在。
- 其实,从底层运行机制来看,没有多维数组。2、二维数组的使用(6个基本点)
>二数组的声明和初始化
>调用二数组的指定元素
>二数组的属性:length,表示数组的长度
>二数组的遍历
>二数组的默认初始化值
>二维数组的内存解析3、二维数组元素的默认初始化值
3.1 动态初始化方式1:(比如:int[][] arr = new int[3][4];)
外层元素,默认储存地址值
内层元素
>整数型数组元素的默认初始化值:0
>浮点型数组元素的默认初始化值:0.0
>字符型数组元素的默认初始化值:0或'\u0000'
>布尔型数组元素的默认初始化值:false
>引用型数组元素的默认初始化值:null
3.2 动态初始化方式2:(比如:int[][] arr = new int[3][];)外层元素,默认储存null
内层元素,不存在的。如果调用报错(NullPointerException)
基本使用//1.数组的声明和初始化
//复习:
int arr1[] = new int[]{1,2,3};
//声明数组
//数组的初始化
//静态初始化:数组变量的赋值与数组元素的赋值操作同时进行
int arr2[][] = new int[][]{{1,2,3},{4,5},{6}};
//数组声明和初始化
//动态初始化:数组变量的赋值与数组元素的赋值操作分开进行
String[][] arr3 = new String[4][3];
//动态初始化2:
double[][] arr4 = new double[4][];
//其它正确方式
int[][] arr5 = new int[][]{{1},{2}};
int[] arr6 []= new int[][]{{1},{2}};
//类型推断
int[][] arr7 ={{1},{2}};
//2.数组元素的调用
//通过角标的方式,获取数组的元素
//角标范围从0到数组长度-1
// 针对arr2来说,外层元素:{1,2,3},{4,5,6},{7,8,9},内层元素:123456789
//调用内层元素
System.out.println(arr2[0][0]);//7
//调用外层元素
System.out.println(arr2[0]);//[I@1b6d3586
//测试arr3、arr4
arr3[0][0]="tom";
System.out.println(arr3[0][0]);//tom
System.out.println(arr3[0]);//[Ljava.lang.String;@4554617c
arr4[0] = new double[4];
arr4[0][0] = 1.0;
//3.数组长度:用来描述数组容器中容量的大小
//使用length属性表示
System.out.println(arr2.length);//根据外层长度(元素个数)判断
System.out.println(arr2[0].length);//3
System.out.println(arr2[1].length);//2
System.out.println(arr2[2].length);//1
//4.如何遍历数组
for (int i = 0; i < arr2.length; i++) {
for (int j = 0; j < arr2[i].length; j++) {
System.out.print(arr2[i][j] + "\t");
}
System.out.println();
}