JAVA学习
章节
第一章:JAVA简介
1.1Java简介
java语言是解释性语言。java很特殊,java程序也需要编译,但是没有直接编译称为机器语言,而是编译称为字节码,然后用解释方式执行字节码。Java既可以被编译,也可以被解释。通过编译器,可以把Java程序翻译成一种中间代码 - 称为字节码 - 可以被Java解释器解释的独立于平台的代码。通过解释器,每条Java字节指令被分析,然后在计算机上运行。只需编译一次,程序运行时解释执行。
1、所有的Java程序文件的后缀都应该是“.java",而任何一个“.java"程序首先必须经过编译,编译之后会形成一个“.class"文件(字节码文件),而后在计算机上执行,但是解释程序的计算机并不是真正意义上的计算机,而是由软件和硬件模拟出来的计算机——Java虚拟机(Java Virtual Machine,JVM),所有的Java程序都是在JVM上运行的,JVM读取并处理经过编译的与平台无关的“.class"文件(字节码文件),Java解释器负责将Java虚拟机的代码在特定的平台上运行。所有要解释的Java程序主要在JVM上执行
javac.exe是java本身提供的编译命令,主要目的是将*.java文件编译成*.class 文件
1.2第一个程序
```java
public class hello {
public static void main(String []args){
System.out.println("hello");
System.out.println("world");
System.out.println("!!!");
}
}
``
1.2第一个程序的解释
2、关于类的定义
类是Java中的基本组成元素,而所有的Java程序一定要被类管理
一个Java文件中基本上只包含一个public class,而不会有其他class单独定义的类
类的·命名规范:所有的类名称都有自己的命名规范,每一个单词的开头字母都必须大写,例如:TestDemo
2、主方法:main()
主方法是一个程序的起点,所有的程序代码都由此开始顺序执行,在Java中主方法也要放在一个类中,定义格式如下:
public static void main(string args[]){
// 编写程序代码;
}
主方法中存在许多字母,这些字母的顺序是固定的,
3、系统输出:System.out.println()
在屏幕上显示输出信息
输出后加换行:System.out.println();
输出后不换行:System.out.print();
完整语句用 ; 结束(和c语言中一样)
Java程序分为两个操作:
1、编译程序:javac Hello.java,此时会形成Hello.class文件,这就属于字节码文件
2、解释程序:java Hello
所有的java程序都有一个最为核心的单元:类
1、public class 类名称{}:文件名必须与类名称一致,一个*.java文件中只能有一个public class ;
2、class 类名称{}:文件名可以和类名称不一致,但是生成的*.class是根据文件中定义的class名称一致的
3、 简述Java中path及classpath的作用。
答: PATH:是操作系统的环境属性,指的是可以执行命令的程序路径;
CLASSPATH:是所有*.class文件的执行路径,java命令执行的时候将利用此路径加载所需要的*.class文件。
2.1 java的注释(注释的这部分内容不会被编译器编译,编译器自动忽略
//:单行注释,在注释内容前面加双斜线//
/* …./:多行注释
/**…./:文档注释,用这种方法注释的内容将会被解释成程序的正式文档,并能包含进如javadoc工具生成的文档里。用以说明该程序的层次结构以及其方法
第二章:程序基本概念
2.2标识符与关键字
1 、public class 类名称,
这里的类名称就属于标识符的内容,,除了类名称外,属性名称、方法名称等也属于标识符的定义范畴
2、标识符的定义要求:
标识符由字母、数字、_ 、$ 组成,其中不能由数字开头,不能是Java的关键字
注意:
编写时尽量不使用数字· 命名尽量有意义
Java中的标识符是区分大小写的 不要使用$符号
关键字:具备特殊含义的单词,例如:public,class,static,关键字全部用小写字母表示
2.3数据类型的划分
不同的数据类型可以保持不同的数据内容,Java的数据类型可以划分为基本数据类型和引用数据类型,
基本数据类型包括最基本的byte、short、int、long、float、double、char、boolean等
引用数据类型(类似与c/c++的指针),在操作时必须要进行内存的开辟
基本数据类型不牵扯内存分配问题,而引用数据类型需要开发者为其分配内存空间。
2.3.1 整型 (和c语言中相似,两个整型数据在进行运算时会触发整型运算)
任何一个数字常量(如30),在Java中都属于int数据类型,在Java中所有设置的整数内容默认情况下都是int型数据
定义变量格式: 数据类型 变量名称 = 常量;
整型(int)和长整型(long) 一起运算时,由于long类型保存的数据范围比较大,所以在计算时int数据类型将统一自动转换为long数据类型后再进行运算
例如:
Public class test{
public static void main(String []args){
int max =Integer.MAX_VALUE; //取出int型变量中的最大值 2147483647
int min =Integer.MAX_VALUE; //取出int型变量中的最小值 -2147483648
//int型变量+long型变量=long型变量;
System.out.println(max+1); //输出结果为2147483648
System.out.println(min-1); //输出结果为-2147483649
}
}
2.3.2浮点数
浮点数就是小数,Java中默认小数的数据类型是double,double是保存数据范围最广的数据类型
由于默认的小数类型是double,所以如果要使用float型时就必须进行强制转换,强制转换有两种方式:1、在小数后加 F 或者 f, 2、在变量或常量前使用(float);
2.3.3字符型
Java中char是两字节的(和c语言不同,c语言中的char是一字节的),Java中char与int型之间的转换和c语言中的相似
Java使用的是十六进制的UNICODE编码,此类编码可以保存任意的文字,此编码包含了ASCII的部分编码(其中每个中文汉字在UNICODE编码表里都有对应得UNICODE码
2.3.4 布尔型
布尔型是一种逻辑型结果,主要保存 ture和false两类数据,这类数据主要用于一些程序得逻辑使用
在Java中,不允许使用0或1来填充布尔型得变量内容
2.3.5 String型数据
String属于引用型数据类型,(它属于一个类,在Java中所有类名称得单词首字母都必须大写)
String是字符串,声明内容时要用双引号“”;(和c语言类似)
Java中可以使用+来进行字符串的连接,
例如:
String str=“Hello”;
str=str+“World!”
System.out.println(str); //输出结果为Hello World!
2.4运算符
根据运算符的使用的类,运算符可以分为以下几种:
赋值运算符、算术运算符、关系运算符、逻辑运算符、条件运算符、括号运算符等
2.4.1关系运算符
关系运算符主要用于数据的大小关系比较,返回的结果是boolean型数据(只有ture和false两种取值),常用的关系运算符有大于(>),小于(<),大于等于(>=),小于等于(<=),等于(==),不等于(!=)
关系运算符往往是结合后续的分支、循环等程序逻辑控制语句使用
2.4.2数学运算符
%只能用于整型数据运算,
注意自增、自减运算符因位置不同而导致的运算顺序的不同(规则和C语言中的相同):
1、++变量,–变量:先在前面的表示的是先进行变量内容的自增1或自减1,再使用改变之后的变量内容来进行数学计算
2、变量++,变量–:先使用原来的变量内容进行数学运算,再进行自增或自减运算
2.4.3三目运算符
基本结构:
数据类型 变量 =布尔表达式 ? 满足表达式时设置的内容 : 不满足表达式时设置的内容; (布尔表达式其实就是逻辑表达式,成立取前者内容,不成立取后者内容)
2.4.4 逻辑运算
逻辑运算一共包含3种:与(多个条件一起满足)、或(多个条件中有一个满足)、非(使用“!”操作,可以实现ture变false的结果转换)
逻辑运算符中的&&和&的区别,||和|的区别:
&&和&
&&和&都可以表示逻辑与,但他们是有区别的,共同点是他们两边的条件都成立的时候最终结果才是true;
不同点是&&只要是第一个条件不成立为false,就不会再去判断第二个条件,最终结果直接为false,而&判断的是所有的条件;
测试&&:
- package test;
- public class Test{
- public static void main(String[] args){
- int i = 23;
- int j = 21;
- if ((i == j) && (100 / 0 == 0))
- System.out.println(“1”);
- else
- System.out.println(“没有报错”);
- }
- }
输出的结果为:没有报错
正常情况下100 / 0 == 0是会报错的,而上面的示例就没有报错,就是因为&&的第一个条件不成立,后面的一个条件被短路了,所以程序没有报错
来自 https://blog.csdn.net/lz199719/article/details/84075759
-
package com.test;
-
public class Test {
-
public static void main(String[] args) {
-
int i = 23;
-
int j = 23;
-
if ((i == j) && (100 / 0 == 0))
-
System.out.println(“1”);
-
else
-
System.out.println(“没有报错”);
-
}
-
}
当把i和j的值改成一样的时候,程序就会报错了 -
Exception in thread “main” java.lang.ArithmeticException: / by zero
-
at com.test.Test.main(Test.java:8)
说明&&的第一个条件成立的时候还会去判断第二个条件,两个条件都成立的时候最终结果才是true
测试&: -
package com.test;
-
public class Test {
-
public static void main(String[] args) {
-
int i = 23;
-
int j = 21;
-
if ((i == j) & (100 / 0 == 0))
-
System.out.println(“1”);
-
else
-
System.out.println(“没有报错”);
-
}
-
}
-
package com.test;
-
public class Test {
-
public static void main(String[] args) {
-
int i = 23;
-
int j = 23;
-
if ((i == j) & (100 / 0 == 0))
-
System.out.println(“1”);
-
else
-
System.out.println(“没有报错”);
-
}
-
}
上面的两段代码的结果都会报错,证明&是判断所有的条件
来自 https://blog.csdn.net/lz199719/article/details/84075759
||和|
||和|都表示逻辑或,共同点是只要两个判断条件其中有一个成立最终的结果就是true,区别是||只要满足第一个条件,后面的条件就不再判断,而|要对所有的条件进行判断。
测试||:
-
package com.test;
-
public class Test {
-
public static void main(String[] args) {
-
int i = 23;
-
int j = 23;
-
if ((i == j) || (100 / 0 == 0))
-
System.out.println(“1”);
-
else
-
System.out.println(“没有报错”);
-
}
-
}
输出结果为:1
测试|: -
package com.test;
-
public class Test {
-
public static void main(String[] args) {
-
int i = 23;
-
int j = 23;
-
if ((i == j) | (100 / 0 == 0))
-
System.out.println(“1”);
-
else
-
System.out.println(“没有报错”);
-
}
-
}
程序会报错
来自 https://blog.csdn.net/lz199719/article/details/84075759
2.4.5位运算 和C语言类似
2.5程序逻辑控制
一般来说程序的结构包括顺序结构、选择结构、循环结构(共同点:都只有一个·入口、一个出口)
2.5.1分支结构
1、第一组选择结构
if、if…else、if…else if…else if…else,三种结构 用法和C语言相同
2、第二组选择结构
switch结构
switch中可以判断 数值、字母、枚举、字符串,但避免因版本问题出错,一般只用于判断 数值和字母
因为switch语句默认情况下会从第一个满足的case语句开始执行全部的语句代码,一直到整个switch语句结束或者遇到break,所以应该在每个case语句后加上break;
注意:if可以判断布尔表达式,switch只能判断内容
2.5.3 循环结构(和C语言相同)
有while循环和for循环
1、while循环
while循环和do…while循环
do…while循环时先执行后判断,而while循环时先判断后执行
如果循环条件都不满足的情况下,do…while循环至少执行一次,while循环一次都不执行
循环的结构特点:
1、循环的结束判断
2、每次循环执行之后,循环条件要求修改(所有的循环语句里都必须有循环的初始化条件,每次循环的时候都要去修改我这儿条件,以判断循环是否结束)
2、for循环
for(循环初始化条件;循环判断;循环条件变更)
{
循环语句;
}
2.5.3循环控制
continue:结束本次循环,不执行下面语句,直接进行下次循环
break:结束整个循环
2.6 方法的定义和使用
方法在很多地方又称为函数(在Java中的英文单词是method,在其他语言中的英文单词是function)
方法的主要功能是封装可以执行的一段代码,这样不仅可以实现重复调用,更可以方便地实现代码地维护
方法的定义格式:(主要还是和c语言相似)
Public static 返回值类型 方法名称 (参数类型 参数变量,…)
{
方法体(本方法要执行的操作);
[return [返回值]; ]
}
返回值类型可以有两种:
1、直接设置的Java中的数据类型(基本数据类型、引用数据类型),如果方法·设置了返回值,那么必须使用return语句来返回与数据类型相对应的数据;
2、方法没有返回值(void),可以不适用return来返回内容,但可以使用return来结束方法的调用;
Java中方法名的命名规范:
第一个单词的首字母小写,之后的每个单词的首字母大写;例如:printlnInfo();
2.6.2方法的重载
方法的重载是指方法名称相同,但参数的类型或个数不同,调用时会按照传递的参数类型和参数个数来完成不同方法体的调用和执行;
假如有一个方法名称,有可能要执行多种操作,但是不同的操作使用的参数类型和参数个数不同,这个时候就要用到方法重载概念的支持
方法重载之后执行语句会根据传入参数的类型和个数的不同来调用不同的方法体;
提示:之前使用的System.out.println()和System.out.print()其实也是·方法重载;
说明:
1、在进行方法重载时一定要考虑到参数类型的统一,虽然可以实现重载方法返回不同的数据类型的操作,但从表尊的开发来讲,建议所有重载之后的方法使用同一种返回值类型;
2、方法重载的时候的重点时根据参数类型以及个数来区分不同的方法,而不是依靠返回值类型的不同来决定的
2.6.3方法的递归掉调用
进行递归操作时必须满足一下两个条件:
1、必须有结束条件(临界条件)
2、每次调用时都需要改变传递的参数
第三章:面向对象基本概念
3.1面向对象基本简介
在Java中方法是不可以单独存在的,必须将其放在一个类中
面向过程:指的是针对于某一个问题单独提出解决方案以及代码开发。
面向对象:以一种组件化的形式进行代码的设计。
利用面向对象设计的程序可以很好地实现代码的重用操作,同时也利于开发者进行项目的维护,面向对象的核心概念组成就是类与对象
面向对象采用的更多是进行子模块化设计,每一个模块都需要单独存在,并且可以反复利用,所以,面向对象的开发更像是一个具备标准模式的编程开发,每一个设计的子模块都可以单独存在,需要时只要通过简单的组装即可使用。
面向对象的程序设计有封装性、继承性、多态性3个主要特征。
3.2类与对象
3.2.1类与对象的基本概念
在面向对象中类和对象是最基本最重要的组成单元。
类实际上是表示一个客观世界中某类群体的一些基本特征抽象,属于抽象的概念集合。
对象就是表示一个个具体的事物,一个个独立的个体。
例如:
1、类:人(因为此处人是一个广义的概念,并不是一个具体的个体描述)
对象:具体的某一个人:张三
属性:各种用来完整地描述这个具体的人的信息
方法(在类中):人产生的各种行为:吃饭、睡觉、打游戏,
2、类:汽车的设计图纸
对象:通过图纸这个模型制造出来的每一辆具体的汽车
属性:汽车的各种特性
类和对象的区别:类实际上是对象操作的模板。但是类不能过直接使用,必须通过实例对象来使用
如果要使用一个类,就一定会产生对象,每个对象之间是靠各个属性的不同来进行区分的,而每个对象所具备的操作就是类中规定好的方法。
(其实这里有点像c语言中的结构体和结构体数组的关系,其中类有点像结构体,对象有点像结构体数组,但是Java中的类包括了单独存在的方法(c语言中的函数),(当然,这只是个人暂时比较浅薄的认识))
3.2.2类与对象的定义
类是由属性和方法组成的,属性中定义类的一个个具体信息,实际上属性就是一个变量,而方法是一些操作的行为。
定义类的规范:
1、要用class关键字
2、语法:
class 类名称{
数据类型 变量(属性);
…
public 返回值的数据类型 方法名称(参数 1,参数2…){
程序语句;
[return 表达式;]
}
}
可以看到,在这个类中方法的定义中,我们没有加上static,
注意:在之前的讲解中:在主类中定义,并且由主方法直接调用的方法必须加上static。
而现在定义的类的调用形式有所改变:我们这里定义的类中的方法将会由对象调用
暂时可以理解为:由对象调用的方法要加上static,不是由对象调用的方法要加上static
类定义之后是无法直接使用的,如果要使用,必修依靠对象,由于类是引用数据类型,所以其对象有特定的定义格式:
1、:声明并实例化对象
类名称 对象名称 =new 类名称();
2、分布完成
声明对象: 类名称 对象名称 =null;
实例化对象: 对象名称 =new 类名称 ();
对使用new这个关键字的解释:
类是引用数据类型,使用前必须为其开辟内存空间,关键字new就是用来开辟空间的
当一个·对象实例化之后就可以按一下方式利用对象来操作类的结构:
1、对象.属性:表示要操作类中的属性内容
2、对象.方法():表示要调用类中的方法 (有点像c语言中的结构体成员的调用)
类本身属于引用数据类型,而对于引用数据类型的执行分析就必须结合内存操作来看,其中涉及了两种内存:
1、堆内存(heap):保存每一个对象的属性内容,堆内存需要用关键字new来进行开辟空间,如果一个对象没有对应的的堆内存指向,将无法使用。
2、栈内存(stack):保存的是一块堆内存的地址数值,可以把它想象成一个int变量(每一个int型变量只能存放一个数值),所以每一块栈内存只能保留一块堆内存地址。
关于堆内存与栈内存的补充说明:
1、堆内存:保存对象的真正数据,都是每一个对象的属性内容;
2、栈内存:保存的是一块堆内存的空间地址,但是为了方便的理解,可以简单地将栈内存中保存的数据理解为对象的名称(但最好还是不要这样理解,就像语言中的地址一样,必须深入准确理解才能更好的理解程序的运行机制)
如果想开辟堆内存空间,就只能依靠关键字来new来进行开辟,即:只要看见了关键字new,不管任何情况下,都表示要开辟新的内存空间。
使用对象时一定要一块对应的堆内存空间,而堆内存空间的开辟需要通过关键字new来实现,每一个对象在实例化之后,里面所有属性的内容都是其对应的数据类型的默认值,只有设置了属性内容之后,属性才可以保存内容。
如果只是声明了对象,但没有进行对象的实例化操作(没有使用关键字new实例化对象),则程序执行之后会报错——报错NullPointerException(空指向错误)的信息,而且Java中出现这种异常的原因只有一个,即在使用引用数据类型时没有为其开辟堆内存空间。
3.2.3引用数据的初步分析
引用传递是整个Java中的精髓所在。
引用传递的核心概念(只有一点):一块堆内存空间(保存对象的属性内容)可以同时被多个栈内存共同指向,则每一块栈内存都可以修改同一块堆内存空间的属性内容。
在所有的引用分析中,最关键的还是关键字new。
一定要注意:每一次使用关键字new都一定会开辟新的堆内存空间,所以如果在代码中里声明了两个(多个)对象,并且分别使用了关键字new为每一个对象进行实例化操作,那么这些对象一定是各自占有各自的堆内存空间,并且不会互相影响。
对引用传递的比喻理解:
引用传递就好比一个人有多个名字:
比如:张三还叫“小三”、“狗子”
当张三的腿断了,小三和狗子的腿j肯定也断了
这是因为不同的名字(栈内存)都指向了同一个实体(堆内存)
代码示例分析:第一行代码Java: P78-P82
3.3封装性分析
封装是属于面向对象的第一大特性,封装涉及内容过多。
对代码进行封装的必要性:
A、封装的作用
1、 对象的数据封装特性彻底消除了传统结构方法中数据与操作分离所带来的种种问题,提高了程序的可复用性和可维护性,降低了程序员保持数据与操作内容的负担
2、对象的数据封装特性还可以把对象的私有数据和公共数据分离开,保护了私有数据,减少了可能的模块间干扰,达到降低程序复杂性、提高可控性的目的。
B、好处
1、提高了安全性
2、提高了复用性
3、隐藏了实现细节
4、模块化:封装分为属性封装,方法封装,类封装,插件封装,模块封装,系统封装等等。
有利于程序的协助分工,互不干扰,方便了模块之间的相互组合与分解,也有利于代码的调试和维护。
• 关键字private
被private修饰的成员只能在本类中使用
• 对属性进行安全性的封装
实现步骤
1、对属性进行私有化
2、对外提供公开的set/get方法
3、如果该属性需要安全性的判断 将这些代码写在set中
来自 https://blog.csdn.net/oqqHei1234567/article/details/83350322
使用关键字private进行封装,将类中的属性进行私有化的操作,被private修饰的成员只能在本类中使用。
package com.company;
class Book{ //定义一个新的类
private String name; //书的名字
private double price; //书的价格
public void getInfo(){ //此方法将有对象调用(用来输出书的信息)
System.out.println("书名:"+name+",价格:"+price);
}
}
public class lkcfzc {
public static void main(String []args)
{
Book bk=new Book(); //声明并实例化对象
bk.name="java开发"; //设置属性内容
bk.price=-89.0; //设置属性内容
bk.getInfo(); //调用方法
}
}
程序编译结果:
报错——(两个错误)
D:\java程序\src\com\company\lkcfzc.java:14:11
java: name 在 com.company.Book 中是 private 访问控制
java: price 在 com.company.Book 中是 private 访问控制
程序分析:
本程序在声明Book类的属性时使用了关键字private,这样就表示name和price两个属性只能在Book类中被访问,而其他类不能访问,所以在主类中使用Book对象直接调用name和price属性时编译报错
为了让外部程序调用类内部属性,针对属性有这样的定义:
所有在类中定义的属性都要用关键字private来声明,如果外部程序要调用这些属性,那么类中必须按照要求定义相应的setter、getter方法:
setter方法主要是设置内容:public void 方法名称(参数…);
getter方法主要是取得属性内容:public 返回数据类型 方法名称(参数…); (set和get方法提供了类和外部的接口)
范例3-10为Book类中的封装属性设置setter和getter操作
package com.company;
class Book{ //定义一个新的类
private String name; //书的名字
private double price; //书的价格
/**
* 设置或修改类中的属性内容
* @param str
*/
public void setName(String str) {
name=str; //设置属性name的内容
}
public void setPrice(double n) {
if (n > 0) //进行数据验证
{
price=n;
}
else System.out.println("输入数据错误!");
}
/**
* 取得类中的属性内容;
* @return
*/
public String getName(){
return name; //取得属性name的内容
}
public double getPrice(){
return price; //取得属性price的内容
}
/**
* 输出所有属性的信息
*/
public void getInfo(){ //此方法将有对象调用(用来输出书的信息)
System.out.println("书名:"+name+",价格:"+price);
}
}
public class lkcfzc {
public static void main(String []args)
{
Book bk=new Book(); //声明并实例化对象
bk.setName("java开发"); //利用类中的setter方法设置属性(name)内容
bk.setPrice(-89.0); //利用类中的setter方法设置属性(price)内容
bk.getInfo(); //调用方法
}
}
程序执行结果:
D:\360downloads\bin\java.exe "-javaagent:D:\360downloads\IntelliJ IDEA Community Edition 2020.3.2\lib\idea_rt.jar=56220:D:\360downloads\IntelliJ IDEA Community Edition 2020.3.2\bin" -Dfile.encoding=UTF-8 -classpath D:\java程序\out\production\java程序 com.company.lkcfzc
输入数据错误! (说明数据验证操作正确)
书名:java开发,价格:0.0 (默认价格为0.0)
程序分析:
本程序在定义Book类时,为封装的name和price两个属性分别定义了各自的setter和getter方法(并且可以在进行属性赋值时进行数据的检查),这样在主类中利用Book类对象调用相应的方法进行设置。
注意:虽然在本程序中未使用getter方法,但在定义类时依旧不能省略这个方法的定义,
setter和getter方法在定义时一定要有,无论会不会用到。这是项目开发的标准做法。
数据验证方面的代码在实际开发中也会更加复杂,实际开发中setter方法一般只是简单的设置数据,getter方法一般只是简单的取得数据。
3.4构造方法
实例化对象时,要用到关键字new来完成,但是除了new这个关键字外,还有可能在对象实例化时为其进行一些初始化的准备操作,这个时候就要用到构造方法。
构造方法本身是一种特殊的方法,它只在新对象实例化的时候调用。
定义原则:
方法名称和类名称相同,没有返回值类型声明(特别注意是没有声明),同时构造方法也可以进行方法重载。
提示:构造方法一直存在。
实际上在对象实例化的格式中就存在构造方法的使用,下面通过对象的实例化格式来分析。
①类名称②对象名称=③new④类名称();
·①类名称:规定了对象的类型,即对象可以使用哪些属性与方法,都是由类定义的;
·②对象名称:如果要想使用对象,需要有一个名字,这是一个唯一的标记;
·③new:开辟新的堆内存空间,如果没有此语句,对象无法实例化;
·④类名称():调用了一个和类名称一样的方法,这就是构造方法。
通过以上的简短分析可以发现,所有的构造方法实际上一直在被我们调用。但是我们从来没有去定义一个构造方法,之所以能够使用构造方法,是因为在整个Java类中,为了保证程序可以正常的执行,即使用户没有定义任何构道方法,也会在程序编译之后自动地为类增加一个没有参数、没有方法名称、类名称相同、没有返回值的构造方法。
范例3-11定义构造方法
程序分析:
本程序在Book类中定义了一个构造方法,构造方法的名称和类的名称相同,没有返回值类型声明,并且构造方法只有在使用关键字new实例化对象是才会被调用一次。
构造方法没有返回值,但是不能使用void来声明,如果使用了void就不是构造方法了,而是普通的方法,因为使用了void之后构造方法和普通方法的定义结构截相同了,而Java程序编译时依靠定义结构来解析的,所以不能用void来声明构造方法。
构造方法和普通方法(两种不同类型的方法)的区别:
调用时机不同:
1、构造方法只有在实例化对象(new)时被调用一次(构造方法时在使用关键字new的时候直接调用的)
2、普通方法是在实例化对象之后,通过“对象.方法”调用多次
构造方法的主要功能就是进行对象初始化操作,所以要是希望在对象实例化时进行属性的初始化赋值操作,就可以使用构造方法。当然,在使用构造方法进行初始化赋值之后还是可以调用setter方法来更改属性内容的。
范例-3-12 利用构造方法为属性初始化赋值
package com.company;
class Book{ //定义一个新的类
private String name; //书的名字
private double price; //书的价格
/**
* Book类构造方法,用于设置name和price的内容
*/
public Book(String str,double n){
name=str;
price=n;
}
/**
* 设置或修改类中的属性内容
* @param str
*/
public void setName(String str) {
name=str; //设置属性name的内容
}
public void setPrice(double n) {
if (n > 0) //进行数据验证
{
price=n;
}
else System.out.println("输入数据错误!");
}
/**
* 取得类中的属性内容;
* @return
*/
public String getName(){
return name; //取得属性name的内容
}
public double getPrice(){
return price; //取得属性price的内容
}
/**
* 输出所有属性的信息
*/
public void getInfo(){ //此方法将有对象调用(用来输出书的信息)
System.out.println("书名:"+name+",价格:"+price);
}
}
public class lkcfzc {
public static void main(String []args)
{
Book bk=new Book("java开发",89.0); //声明并实例化对象
// bk.setName("java开发"); //设置属性内容
//bk.setPrice(-89.0); //设置属性内容
bk.getInfo(); //调用方法
}
}
程序执行结果:书名:java开发,价格:89.0
构造方法的核心作用:
在实例化对象时设置属性的初始化内容。构造方法就是为属性初始化准备的,类中定义了一个构造方法时,在对象实例化时系统将不会在默认生成构造方法,即一个类中至少保留一个构造方法,所以在定义了构造方法之后在实例化对象时一定要注意是否正确使用已定义的构造方法。
类中的结构包括:属性,构造方法,普通方法, 其中setter和getter方法一般在普通方法中定义
一定要注意编写代码的顺序: 属性(并且要提供对应的setter和getter方法),构造方法,普通方法。
构造方法也是方法,所以也有方法重载,但由于它的特殊性,只需要注意参数的类型和个数问题即可,不用考虑返回值类型。
(和普通方法的重载类似,不再赘述),但要注意一点:
注意编写顺序:按照参数个数从少到多(或从多到少)编写
关于属性默认值的问题:
在定义一个类时,可以为属性直接设置默认值,但是这个默认值只有在构造执行完之后才会设置,否则不会。
3.5匿名对象
按照之前的定义拉说,对象的名字可以解释为在栈内存中保存(实际上保存的是堆内存的空间地址),而对象的具体内容(属性)则在堆内存中保存。这样一来,没有栈内存指向堆内存空间,这样的一个对象就是匿名对象。
范例3-14 定义匿名对象
package com.company;
class Book1{
private String name;
private double price;
public Book1(String str,double n)
{
name=str;
price=n;
}
//此处省略setter和getter方法的编写
public void getInfo(){
System.out.println("书名:"+name+",价格:"+price);
}
}
public class Main {
public static void main(String[] args) {
new Book1("java开发",89.0).getInfo(); //此处是没有 “类名称 对象名称”的对象实例化,为匿名对象的定义
}
}
程序执行结果:
D:\360downloads\bin\java.exe "-javaagent:D:\360downloads\IntelliJ IDEA Community Edition 2020.3.2\lib\idea_rt.jar=58202:D:\360downloads\IntelliJ IDEA Community Edition 2020.3.2\bin" -Dfile.encoding=UTF-8 -classpath D:\java程序\out\production\java程序 com.company.Main
书名:java开发,价格:89.0 //调用了getInfo()方法
Process finished with exit code 0
程序分析: 虽然是匿名对象,但还是实例化之后才能使用
本程序通过匿名对象调用了类中的方法,但由于匿名对象没有对应的栈内存指向,所以只能使用一次,一次之后就会成为垃圾,并且被GC回收。
3.6简单Java类
简单Java类是一种在实际开发中使用最多的类的定义形式,在简单Java类中包含类、对象、构造方法、private封装等核心概念的使用,简单Java类有如下基本开发要求:
1、类名称必须存在意义:如Book、Emp;
2、类中所有的属性必须private封装,封装之后的属性必须提供setter和getter方法;
3、类中可以提供任意多个构造方法,但是必须保留一个无参数的构造方法;
4、类中不允许出现任何输出语句,所有信息输出必须交给被调用处输出;
5、类中需要提供一个可以取得对象完整信息的方法,暂定为getInfo(),而且返回String型数据;
下面利用一个雇员类来巩固简单类的使用:
范例3-15 开发Emp程序类
package com.company;
class Emp{
private String empno;
private String ename;
private String job;
private double sal;
private double comm;
public Emp(){} 无参构造
public Emp(String str1,String str2,String str3,double sal1,double comm1){
empno=str1; 有参构造
ename=str2;
job=str3;
sal=sal1;
comm=comm1;
}
public void setEmpno(String empno) {
this.empno = empno;
}
public void setEname(String ename) {
this.ename = ename;
}
public void setJob(String job) {
this.job = job;
}
public void setSal(double sal){
this.sal=sal;
}
public void setComm(double comm) {
this.comm = comm;
}
public String getEmpno() {
return empno;
}
public String getEname() {
return ename;
}
public String getJob() {
return job;
}
public double getSal() {
return sal;
}
public double getComm() {
return comm;
}
public String getInfo(){
return "雇员编号:"+empno+"\n"+"雇员姓名:"+ename+"\n"+"雇员职务:"+job+"\n"+"雇员工资:"+sal+"\n"+"雇员佣金:"+comm;
}
}
public class Test {
public static void main(String[] args){
Emp emp1=new Emp("2200400107","chenfucheng","student",30000,1.0);
System.out.println(emp1.getInfo());
}
}
程序执行结果:
D:\360downloads\bin\java.exe "-javaagent:D:\360downloads\IntelliJ IDEA Community Edition 2020.3.2\lib\idea_rt.jar=58653:D:\360downloads\IntelliJ IDEA Community Edition 2020.3.2\bin" -Dfile.encoding=UTF-8 -classpath D:\java程序\out\production\java程序 com.company.Test
雇员编号:2200400107
雇员姓名:chenfucheng
雇员职务:student
雇员工资:30000.0
雇员佣金:1.0
Process finished with exit code 0
3.7数组
数组可以将多个变量进行统一的命名,这样相同类型的元素就可以按照一定的顺序进行排列。
在Java中,数组属于引用数据类型,所以在数组的操作过程中,也一定会牵扯到内存的分配问题。
3.7.1数组的基本概念
数组指的就是一组相关变量的集合。
当需要定义多个整型变量时,我们可以使用int 变量1,变量2,变量3…来进行,但是这样定义的变量都是独立分散开来的,不容易进行管理,此时就可以考虑数组来解决问题。
数组本身就是引用数据类型,定义结构:
1、声明并开辟数组:
数据类型 数组名称 []=new 数据类型 [长度]; (和类的定义结构类似)
数据类型 [] 数组名称 =new 数据类型 [长度];
2、分步完成:
声明数组: 数据类型 数组名称 []=null;
开辟数组: 数组名称 =new 数据类型 [长度];
当数组开辟空间后就可以利用下标或者索引的方式来进行访问数组里的内容,访问方式为: 数组名称.[下标(索引)],
注意:数组的下标都是从0开始的,如果访问的时候超过了数组允许的下标长度,就会出现数组越界异常(Array Index Out Of Bounds Exception)(一定要小心数组越界的问题)
数组是一种顺序结构,并且数组的长度都是固定的,所以可以采用循环(一般采用for循环)的方式输出其中的内容。
Java为了方便数组的输出,提供了一个 “数组名称.length”的属性来取得数组的长度
范例3-17 定义数组
package com.company;
public class ArrayTest {
public static void main(String [] args){
int data[]=new int[3]; //声明并开辟了一个长度为3的数组
data[0]=1;
data[1]=2;
data[2]=3;
int temp[]=new int [3]; //定义了一个和·=data数组同样长度的数组
temp=data; //数组引用传递(另这两块栈内存指向同一块堆内存)
temp[0]=99; //利用temp数组来进行修改数组内容
int i;
for(i=0;i<data.length;i++) //循环输出数组的内容
{
System.out.println(data[i]);
}
}
}
程序执行结果:
D:\360downloads\bin\java.exe "-javaagent:D:\360downloads\IntelliJ IDEA Community Edition 2020.3.2\lib\idea_rt.jar=59817:D:\360downloads\IntelliJ IDEA Community Edition 2020.3.2\bin" -Dfile.encoding=UTF-8 -classpath D:\java程序\out\production\java程序 com.company.ArrayTest
99
2
3
Process finished with exit code 0
动态初始化数组:
开辟新的数组,对数组的每一个元素进行赋值。
静态初始化数组,格式如下:
1、简化格式: 数据类型 数组名称 []={值,值,值…}; (和c语言相同)
2、完整格式: 数据类型 数组名称 []=new 数据类型 []{值,值,值…};
范例3-18 静态初始化数组
package com.company;
public class ArrayTest {
public static void main(String [] args){
int data[]=new int[]{1,2,3}; //或者用 int data[]={1,2,3};
int i;
for(i=0;i<data.length;i++) //循环输出数组的内容
{
System.out.println(data[i]);
}
}
}
提示:数组的实际使用
虽然数组支持顺序的数据访问,但是数组有一个最大的缺点——长度不能改变,正因为如此,在开发中才不会直接应用数组,但是会应用数组的概念(利用类集框架来解决)
3.7.2 二维数组(和C语言相似)
二维数组与一维数组最大的区别在于:一维数组声明(还有访问时)只会有一个[],二维数组会有两个[],即[][],
二维数组的定义结构:
1、动态初始化: 数据类型 数组名称 [][]=new 数据类型 [行数][列数];
2、静态初始化: 数据类型 数组名称 [][]=new 数据类型 [][]{{值,值,值…},{值,值,值…},{},…};
二维数组实际上就是将多个一维数组变为一个大的数组,并且为每一个一维数组设置一个行号。
注意:Java中的二维数组不允许像C语言中一样在静态初始化时在大括号里直接编写数值,必须严格按照静态初始化的规定
比如说:
int data[][] = new int[][]{1,1,1,2,2,2,3,3,3};
这样是不允许的,编译时会直接报错
D:\java程序\src\com\company\ArrayTest.java:5:36
java: 不兼容的类型: int无法转换为int[]
注意:不建议使用多维(超过二维)数组
3.7.3 数组与方法参数的传递
既然数组可以进行引用传递,那么就可以把数组传递给方法中的参数,
如果一个方法想要接收数组,那么他的参数也必须是 数组
范例3-22 一个数组传递的程序(数组传递给方法的参数)
package com.company;
public class ArrayTest {
public static void main(String [] args){
int data []=new int[]{1,2,3};
change (data); //将data数组传递给了方法change
int i;
for(i=0;i<data.length;i++)
System.out.print(data[i]+"、");
}
public static void change (int temp[]){ //定义change方法(将数组的每个数乘2)
int i;
for(i=0;i<temp.length;i++)
temp[i]*=2;
}
}
程序执行结果:
D:\360downloads\bin\java.exe "-javaagent:D:\360downloads\IntelliJ IDEA Community Edition 2020.3.2\lib\idea_rt.jar=63603:D:\360downloads\IntelliJ IDEA Community Edition 2020.3.2\bin" -Dfile.encoding=UTF-8 -classpath D:\java程序\out\production\java程序 com.company.ArrayTest
2、4、6、
Process finished with exit code 0
程序分析:
利用数组的静态初始化之后,利用change方法接收次数组,实现了引用传递,相当于方法中定义的temp参数(int数组类型)与主方法中的数组data指向了通一块堆内存空间,最后在change()方法中修改了数组的内容;
范例3-24冒泡排序(升序)
package com.company;
public class ArrayTest {
public static void main(String [] args){
int a[]=new int []{7,3,6,9,2,1,5,4};
int i,j;
int temp;
for(i=0;i<a.length-1;i++)
for(j=0;j<a.length-1-i;j++)
{
if(a[j]>a[j+1])
{
temp=a[j];
a[j]=a[j+1];
a[j+1]=temp;
}
}
for(i=0;i<a.length;i++)
System.out.print(a[i]+"、");
}
}
执行结果:
D:\360downloads\bin\java.exe "-javaagent:D:\360downloads\IntelliJ IDEA Community Edition 2020.3.2\lib\idea_rt.jar=63695:D:\360downloads\IntelliJ IDEA Community Edition 2020.3.2\bin" -Dfile.encoding=UTF-8 -classpath D:\java程序\out\production\java程序 com.company.ArrayTest
1、2、3、4、5、6、7、9、
Process finished with exit code 0
Tip:在开发时,我们编写的主方法一般是客户段,代码应该简洁明了,所以我们一般只会在主方法中进行其他方法的声明,比如或这个冒泡排序,直接编写sort(a);然后在主类之外编写sort这个方法,还有比如说循环输出内容,也可能会直接声明一个print(a);方法
范例3-25选择排序(升序)
范例3-25选择排序(升序)
package com.company;
public class ArrayTest {
public static void main(String [] args){
int a[]=new int []{7,3,6,9,2,1,5,4};
int i,j;
int min,temp;
for(i=0;i<a.length-1;i++)
{
min=i;
for(j=i+1;j<a.length;j++)
{
if(a[j]<a[min]) min=j;
}
temp=a[i];
a[i]=a[min];
a[min]=temp;
}
for(i=0;i<a.length;i++)
System.out.print(a[i]+"、");
}
}
程序执行结果:
D:\360downloads\bin\java.exe "-javaagent:D:\360downloads\IntelliJ IDEA Community Edition 2020.3.2\lib\idea_rt.jar=63741:D:\360downloads\IntelliJ IDEA Community Edition 2020.3.2\bin" -Dfile.encoding=UTF-8 -classpath D:\java程序\out\production\java程序 com.company.ArrayTest
1、2、3、4、5、6、7、9、
Process finished with exit code 0
范例3-26 一维数组的转置
package com.company;
public class ArrayTest {
public static void main(String [] args){
int a[]=new int []{7,3,6,9,2,1,5,4};
int i;
int l=a.length-1;
for(i=0;i<a.length/2;i++)
{
int t=a[i];
a[i]=a[l-i];
a[l-i]=t;
}
for(i=0;i<a.length;i++)
System.out.print(a[i]+"、");
}
}
执行结果:
D:\360downloads\bin\java.exe "-javaagent:D:\360downloads\IntelliJ IDEA Community Edition 2020.3.2\lib\idea_rt.jar=63799:D:\360downloads\IntelliJ IDEA Community Edition 2020.3.2\bin" -Dfile.encoding=UTF-8 -classpath D:\java程序\out\production\java程序 com.company.ArrayTest
4、5、1、2、9、6、3、7、
Process finished with exit code 0
范例3-27 二维数组的转置(行和列数相同)
package com.company;
public class ArrayTest {
public static void main(String [] args) {
int data[][] = new int[][]{{1,1,1},{2,2,2},{3,3,3}};
int i, j;
int temp;
for (i = 0; i < data.length; i++) {
for (j = 0; j < data[i].length; j++) {
System.out.print(data[i][j] + "、");
}
System.out.println();
}
for(i=0;i<data.length-1;i++)
for(j=i;j<data[i].length;j++)
{
temp=data[i][j];
data[i][j]=data[j][i];
data[j][i]=temp;
}
System.out.println("******");
for (i = 0; i < data.length; i++) {
for (j = 0; j < data[i].length; j++) {
System.out.print(data[i][j] + "、");
}
System.out.println();
}
}
}
执行结果:
D:\360downloads\bin\java.exe "-javaagent:D:\360downloads\IntelliJ IDEA Community Edition 2020.3.2\lib\idea_rt.jar=63941:D:\360downloads\IntelliJ IDEA Community Edition 2020.3.2\bin" -Dfile.encoding=UTF-8 -classpath D:\java程序\out\production\java程序 com.company.ArrayTest
1、1、1、
2、2、2、
3、3、3、
******
1、2、3、
1、2、3、
1、2、3、
Process finished with exit code 0
3.7.4数组的操作方法
Java本身针对数组提供了类库的支持;
以下是两个与数组有关的操作方法:
1、数组复制:
数组复制可以将一个数组的部分内容复制到另外一个数组中,语法结构:
System.arraycopy(源数组名称,源数组复制开始索引(下标),目标数组名称,目标数组复制开始索引(下标),长度);
(其中:源数组是要复制数据的来源数组,目标数组是接收复制数据的数组)(这里的结构是经过修改的,之后会有详细介绍)
范例3-27实现数组复制
- [ ] package com.company;
public class ArrayTest {
public static void main(String [] args){
int dataA[]=new int[]{1,2,3,4,5,6,7,8}; //定义源数组
int dataB[]=new int[]{11,22,33,44,55,66,77,88}; //定义目标数组
System.arraycopy(dataA,4,dataB,2,3); //复制数组 将5,6,7复制到了dataB
int i;
for(i=0;i<dataB.length;i++)
System.out.println(dataB[i]);
}
}
程序执行结果:
D:\360downloads\bin\java.exe "-javaagent:D:\360downloads\IntelliJ IDEA Community Edition 2020.3.2\lib\idea_rt.jar=60348:D:\360downloads\IntelliJ IDEA Community Edition 2020.3.2\bin" -Dfile.encoding=UTF-8 -classpath D:\java程序\out\production\java程序 com.company.ArrayTest
11、22、5、6、7、66、77、88、 (实现了数组内容的部分复制)
Process finished with exit code 0
2、数组排序
数组排序可以按照从小到大的顺序对基本数据类型数组(byte,short,int,long,float,double,char)进行排序;
语法结构: java.util.Arrays.sort(数组名称);
提示:先按照语法使用
java.util是一个Java的系统包的名称,Arrays是一个系统提供的类
范例3-28实现排序
package com.company;
public class ArrayTest {
public static void main(String [] args){
int array []=new int []{9,8,7,3,4,6,1,2};
java.util.Arrays.sort(array);
int i;
for(i=0;i<array.length;i++)
System.out.print(array[i]+"、");
}
}
程序执行结果:
D:\360downloads\bin\java.exe "-javaagent:D:\360downloads\IntelliJ IDEA Community Edition 2020.3.2\lib\idea_rt.jar=63017:D:\360downloads\IntelliJ IDEA Community Edition 2020.3.2\bin" -Dfile.encoding=UTF-8 -classpath D:\java程序\out\production\java程序 com.company.ArrayTest
1、2、3、4、6、7、8、9、
Process finished with exit code 0
常见面试问题分析:请编写一个数组排序操作
注意:一定不要直接编写java.util.Arrays.sort(数组名称);这样是不合格得,因为面试者是要靠擦基本素质,所以应该完整编写排序算法:冒泡排序,选择排序等等
3.7.5对象数组
数组是引用类型,而类同样也是引用数据类型,所以如果是对象数组(每一个元素都是对象)得话表示一个引用类型里面嵌套其他的引用类型。
之前定义的数组都属于基本数据类型数组,但是所有的引用数据类型同样可以定义数组,这样的数组成为对象数组。
定义对象数组的格式(这里以类为例子):
1、对象数组的动态初始化:
类名称 对象数组名称 =new 类名称 [长度];
(注意:如果使用了对象数组的动态初始化,默认情况下,数组的每一个元素都是其对应的默认值null,都需要分别进行对象的实例化操作);
2、对象数组的静态初始化:
类名称 对象数组名称 =new 类名称 [] {实例化对象,实例化对象…};
范例 3-29 对象数组的动态初始化:
package com.company;
class Book{
private String name;
private double price;
public Book(){
}
public Book(String str,double n){
name=str;
price=n;
}
public String getInfo(){
return "书名:"+name+",价格:"+price;
}
}
public class ArrayTest {
public static void main(String [] args){
Book array []=new Book [3]; //动态初始化对象数组
array[0]=new Book("java开发",89.0);
array[1]=new Book("Java学习",90.0); 动态初始化之后,对象数组中的每一个元素都得实例化
array[2]=new Book("Java深入",99.0);
int i;
for(i=0;i<array.length;i++)
System.out.println(array[i].getInfo());
}
}
程序执行结果:
D:\360downloads\bin\java.exe "-javaagent:D:\360downloads\IntelliJ IDEA Community Edition 2020.3.2\lib\idea_rt.jar=63506:D:\360downloads\IntelliJ IDEA Community Edition 2020.3.2\bin" -Dfile.encoding=UTF-8 -classpath D:\java程序\out\production\java程序 com.company.ArrayTest
书名:java开发,价格:89.0
书名:Java学习,价格:90.0
书名:Java深入,价格:99.0
Process finished with exit code 0
范例3-30对象数组的静态初始化
package com.company;
class Book{
private String name;
private double price;
public Book(){
}
public Book(String str,double n){
name=str;
price=n;
}
public String getInfo(){
return "书名:"+name+",价格:"+price;
}
}
public class ArrayTest {
public static void main(String [] args){
Book array []=new Book []{
new Book("java开发",89.0), new Book("Java学习",90.0), new Book("Java深入",99.0) //静态初始化对象数组
};
int i;
for(i=0;i<array.length;i++)
System.out.println(array[i].getInfo());
}
}
执行结果:
D:\360downloads\bin\java.exe "-javaagent:D:\360downloads\IntelliJ IDEA Community Edition 2020.3.2\lib\idea_rt.jar=63553:D:\360downloads\IntelliJ IDEA Community Edition 2020.3.2\bin" -Dfile.encoding=UTF-8 -classpath D:\java程序\out\production\java程序 com.company.ArrayTest
书名:java开发,价格:89.0
书名:Java学习,价格:90.0
书名:Java深入,价格:99.0
Process finished with exit code 0
对象数组的最大好处就是可以讲多个对象进行统一管理,并且除了数据类型改变之外,和基本数据类型数组没有任何区别,而且数组本身就是引用数据类型,因此对象数组就是再一个引用数据类型中嵌入其他引用数据类型。
3.8String类的基本概念
String是字符串的描述类型,字符串本身不属于基本数据类型,但是也可以像基本数据类型那样直接赋值
3.8.1String类的两种实例化方式
1、直接赋值:
String 变量 =“字符串”; 例如: String str=“chenfucheng”;
2、利用构造方法赋值:
String 变量 =new String(“字符串”);
(这里实际上是调用了String类中的一个构造方法类进行实例化,这个构造方法的语法如下:public String(String str);
例如:String str=new String(“hello world”);
3.8.2字符串的比较
在Java中如果要比较两个int型数据是否相等,可以直接使用来判定,
在String中,号同样也可以使用,但是注意,此时号比较的不是字符串的内容,而是两个字符串所在的堆内存的空间地址的数值,
范例3-34 在String中使用
public class Test {
public static void main(String[] args){
String stra="hello"; //直接赋值定义字符串
String strb=new String("hello"); //构造方法定义字符串
String strc=strb; //引用传递
System.out.println(stra==strb);
System.out.println(stra==strc);
System.out.println(strb==strc);
}
}
程序执行结果:
D:\360downloads\bin\java.exe "-javaagent:D:\360downloads\IntelliJ IDEA Community Edition 2020.3.2\lib\idea_rt.jar=65225:D:\360downloads\IntelliJ IDEA Community Edition 2020.3.2\bin" -Dfile.encoding=UTF-8 -classpath D:\java程序\out\production\java程序 com.company.Test
false 虽然stra,strb,strc这三个String类的对象中的内容都是hello,但是stru与strb\strc的内存空间不同,所以
false 判定结果是false
true 因为strc通过引用传递和strb指向了同一块堆内存,所以空间地址相同,判定结果为ture
Process finished with exit code 0
程序分析·:在这个程序中可以充分说明,==号在Java的String中比较的是内存地址的数值,而不是字符串的内容
提示·:
在所有数据类型中都可以使用==号来判定,
1、在基本数据类型中:号是判断内容是否相等的,例如33,判定结果为ture;
2、在引用数据类型中:(Java中只要是引用数据类型,就一定会存在内存地址),==号可以用于所有的引用数据类型,但是不会比较内容,永远比较的是内存地址的数值,(这样的操作往往只会出现在判断两个不同名的对象是否指向同一块堆内存空间)
在String类中,想要比较字符串的内容,可以使用String类中定义的方法,(区分·大小写):
public boolean equals(String str);
(Java中String类中定义的equals方法的语法结构是:public boolean equals(Object str);但是这里我们还没学习Object类,所以这里先使用这种形式)
范例3-35 调用equals()方法比较字符串内容
public class Test {
public static void main(String[] args){
String stra="hello"; //直接赋值定义字符串
String strb=new String("hello"); //构造方法定义字符串
String strc=strb; //引用传递
System.out.println(stra.equals(strb));
System.out.println(stra.equals(strc));
System.out.println(strb.equals(strb));
}
}
执行结果:
D:\360downloads\bin\java.exe "-javaagent:D:\360downloads\IntelliJ IDEA Community Edition 2020.3.2\lib\idea_rt.jar=65346:D:\360downloads\IntelliJ IDEA Community Edition 2020.3.2\bin" -Dfile.encoding=UTF-8 -classpath D:\java程序\out\production\java程序 com.company.Test
true
true 说明比较的是·内容,而不是内存地址数值
true
Process finished with exit code 0
常见面试问题:请解释String类种==和equals()的区别:
1、==是Java种提供的关系运算符,主要功能是比较数值,在String对象上表示的是内存地址的比较
2、equals():是由String类提供的一个方法,此方法专门负责进行字符串的比较
3.8.3字符串常量就是String的匿名对象
任何语言都没有提供字符串数据类型的概念,同样在Java中也没有字符串的概念,但是Java创造了自己的特殊类——String,String依旧不属于基本数据类型,所以字符串实际上是作为String类的匿名对象的形式存在的
范例3-36 观察字符串是匿名对象的验证
public class Test {
public static void main(String[] args){
String str ="hello";
System.out.println("hello".equals(str));
}
}
执行结果:
D:\360downloads\bin\java.exe "-javaagent:D:\360downloads\IntelliJ IDEA Community Edition 2020.3.2\lib\idea_rt.jar=49357:D:\360downloads\IntelliJ IDEA Community Edition 2020.3.2\bin" -Dfile.encoding=UTF-8 -classpath D:\java程序\out\production\java程序 com.company.Test
true
程序分析:
本程序最大的特点在于直接利用了字符串"hello"来调用equals()方法,由于equals()是String类定义的方法,而类中的方法只能由实例化后的对象调用,那么这里可以说明:字符串常量就是String类的匿名对象
所谓的String类对象直接赋值的操作,实际上就相当于将一个匿名对象设置了一个名字(让一个栈内存指向了这个匿名对象的堆内存),但是这里的String类的匿名对象是由系统自动生成的,不再是由用户直接创建。
实际开发中比较字符串的小技巧:将要与之比较的字符串常量放在equals()方法的前面,即 字符串常量.equals(要比较的字符串); 这样可以有效避免空指向问题;
3.8.4两种实例化方式的区别
1、分析直接赋值实例化String类对象的情况:
直接赋值就是将一个字符串的匿名对象设置了一个名字,语法结构:
String 变量= “字符串常量”;
例如:String str=“hello”;
此时在内存中会开辟一块堆内存,内存中将保存hello字符串,并且栈内存将直接引用该堆内存,
通过图3-21可知,使用直接赋值的方式为String类对象实例化时只会开辟一块堆内存空间,
除了这一特点外,利用直接赋值还可以实现堆内存空间的重用,即采用直接赋值的方式,在相同内容的情况下不会再开辟新的堆内存空间,而会直接指向已有的堆内存空间。
范例3-39 观察直接赋值时的堆内存自动引用
public class Test {
public static void main(String[] args){
String stra="hello";
String strb="hello";
String strc="hello";
String strd="chen";
System.out.println(stra==strb);
System.out.println(stra==strc);
System.out.println(strb==strc);
System.out.println(stra==strd);
}
}
执行结果:
D:\360downloads\bin\java.exe "-javaagent:D:\360downloads\IntelliJ IDEA Community Edition 2020.3.2\lib\idea_rt.jar=49495:D:\360downloads\IntelliJ IDEA Community Edition 2020.3.2\bin" -Dfile.encoding=UTF-8 -classpath D:\java程序\out\production\java程序 com.company.Test
true
true 说明stra,strb,strc,都指向同一块堆内存(即保存hello内容的堆内存)
true
false 说明strd指向的是另一块堆内存,
Process finished with exit code 0
程序分析:
由于使用了直接赋值的实例化操作方式,设置的内容相同时,及时没有直接发生对象的引用传递,但是stra,strb,strc,也都自动地指向了保存hello的堆内存空间,但是当直接赋值的内容不相同时,则会自动开辟新的堆内存空间。
技术飞越:String类采用的设计模式是共享设计模式
在JVM的底层实际上会存在一个对象池(不一定只保存String对象),当代码中使用了直接赋值的方式定义一个String类对象时,会将此字符串对象所使用的匿名对象放入池中保存。如果后续还有其他String类对象也来用了直接赋值的方式,并且设置了同样的内容时,将不会开辟新的堆内存空间,面是使用已有的对象进行引用的分配,从而继续使用。
2、分析构造方法实例化String类对象的情况
如果要明确的调用String类中的构造方法进行Strig类对象的实例化时,那么一定要使用关键字new,而每当使用关键字new时就表示要开辟新的堆内存空间,这块堆内存空间的内容就是传入到构造方法中的字符串数据。
语法结构:
String 变量名称 =new String (“字符串”);
以String str=“hello”;为例:
因为每一个字符串常量都死一个String类的匿名对象,所以使用构造方法实例化操作时的含义就是:
根据“hello”这个匿名对象的内容创建一个新的String类对象,
分析:因为每一个字符串都是String类的匿名对象,所以首先在堆内存中开辟一块空间保存字符串"hello",然后使用关键字new开辟另一块堆内存空间,因此真正使用的是用关键字new开辟的堆内存,而之前定义的字符串常量的堆内存空间将不会有任何的栈内存指向,成为垃圾,等待被GC回收。所以使用构造方法实例化时会开辟两块堆内存空间,其中有一块将会成为垃圾。
除了内存的浪费之外,由于关键字new始终表示开辟新的堆内存空间,所以其内容不会自动保存在对象池中,即无法重用。
范例3-40 构造方法实例化String类不会自动保存到对象池
public class Test {
public static void main(String[] args){
String stra=new String("helo");
String strb="hello";
System.out.println(stra==strb);
}
}
执行结果:
D:\360downloads\bin\java.exe "-javaagent:D:\360downloads\IntelliJ IDEA Community Edition 2020.3.2\lib\idea_rt.jar=49775:D:\360downloads\IntelliJ IDEA Community Edition 2020.3.2\bin" -Dfile.encoding=UTF-8 -classpath D:\java程序\out\production\java程序 com.company.Test
如果希望利用构造方法实例化的对象也可以进入对象池中以供重用,那就需要用到String类定义的方法,其语法结构如下:
Public String intern();
范例3-41 手动入池
public class Test {
public static void main(String[] args){
String stra=new String("hello").intern();
String strb="hello";
System.out.println(stra==strb);
}
}
执行结果:
D:\360downloads\bin\java.exe "-javaagent:D:\360downloads\IntelliJ IDEA Community Edition 2020.3.2\lib\idea_rt.jar=50385:D:\360downloads\IntelliJ IDEA Community Edition 2020.3.2\bin" -Dfile.encoding=UTF-8 -classpath D:\java程序\out\production\java程序 com.company.Main
true (说明stra和strb指向同一块空间地址)
Process finished with exit code 0
3.8.5字符串一旦定义则不可改变
字符串一旦定义之后是不能改变的,在修改String类对象的内容时其实是发生了引用改变。
范例3-42 修改String类对象引用
public class Test {
public static void main(String[] args){
String str="hello";
str=str+" world";
str=str+"!";
System.out.println(str);
}
}
执行结果:
D:\360downloads\bin\java.exe "-javaagent:D:\360downloads\IntelliJ IDEA Community Edition 2020.3.2\lib\idea_rt.jar=50474:D:\360downloads\IntelliJ IDEA Community Edition 2020.3.2\bin" -Dfile.encoding=UTF-8 -classpath D:\java程序\out\production\java程序 com.company.Test
hello world!
Process finished with exit code 0
程序分析:这里修改了两次str的内容,但其实是发生了两次引用改变,而字符串的内容并没有改变,只是String类对象的引用发生了改变,
可以发现,在进行String类对象的内容修改时,实际上原始的字符串都没有发生改变(最终没有引用的堆内存将成为垃圾),而改变的只是String类对象的引用关系。所以可以得到结论:字符串一旦定义则不能改变,
正因为这样的特性:字符串不能频繁的进行修改(所有的数据类型在遇见String类连接操作是都会自动向String类转换),否则将会产生大量的堆内存垃圾,这时严格禁止的。
例如:
String str="’;
for(int i=0;i<1000;i++) //这里发生了1000次String类对象的引用改变,产生大量垃圾,这是严格禁止的
str+=I;
System.out.println(str);
String类对象的改变实际上是引用关系的改变,当遇到需要频繁修改String类对象内容时:
可以使用两个可以修改内容的字符串类型:StringBuffer,StringBuilder;
3.9String类的常用方法
需要记住:
1、方法名称;
2、返回值类型
3、参数类型及个数
4、作用
Java中的文档组成(当不知道某个方法时就需要查阅文档):
1、类的定义结构及相关的继承结构
2、类的一些简短说明
3、类的组成结构:
成员、构造方法、普通方法
4、对每一个成员、构造方法、普通方法的作用进行详细的说明,包括参数的作用
3.9.1字符与字符串
范例3-44 取出指定索引的字符 ——使用charAt()方法
public class Test {
public static void main(String[] args){
String str="hello";
char ch=str.charAt(0);
System.out.println(ch);
}
}
执行结果:
D:\360downloads\bin\java.exe "-javaagent:D:\360downloads\IntelliJ IDEA Community Edition 2020.3.2\lib\idea_rt.jar=50834:D:\360downloads\IntelliJ IDEA Community Edition 2020.3.2\bin" -Dfile.encoding=UTF-8 -classpath D:\java程序\out\production\java程序 com.company.Test
h
Process finished with exit code 0
分析:charAt()方法的主要作用是从一个字符串中截取指定索引的字符。
范例3-45 字符数组与字符串的转换
public class Test {
public static void main(String[] args){
String str="hello";
char ch[]=str.toCharArray();
int i;
for(i=0;i<ch.length;i++)
System.out.print(ch[i]+"、");
}
}
执行结果:
D:\360downloads\bin\java.exe "-javaagent:D:\360downloads\IntelliJ IDEA Community Edition 2020.3.2\lib\idea_rt.jar=50900:D:\360downloads\IntelliJ IDEA Community Edition 2020.3.2\bin" -Dfile.encoding=UTF-8 -classpath D:\java程序\out\production\java程序 com.company.Test
h、e、l、l、o、
Process finished with exit code 0
对new String(ch),和new String(ch,1,2)的解释:
这两个是String类的构造方法,作用是将字符数组转化为字符串;其中new String(ch)是将全部字符数组转化为字符串,而new String(ch,1,2)是将部分字符数组转化为字符串,(1,2是索引,即下标)
构造方法只能在使用关键字new时使用,即必须在对象实例化时使用,而new String(ch)实际上是创建了一个匿名对象。
这里代码还可以写成:
String str1=new String (ch);
System.out.println(str1);
相当于创建了另一个String类对象
分析:本程序主要实现了字符串的拆分操作,利用toChArray()方法将一个字符串转变为一个字符数组,而拆分后的字符数组的我长度就是字符串的长度。
在利用toCharArray()方法将一个字符串转化为一个字符数组之后,就可以对每一个字符进行操作,下面利用编码值改变来演示将一个字符串小写字母转变为大写字母。
范例3-46 将字符串转为大写
public class Test {
public static void main(String[] args){
String str="hello"; //字符串由小写字母组成
char ch[]=str.toCharArray(); //将字符串转化为字符数组
int i;
for(i=0;i<ch.length;i++)
ch[i]-=32; //改变每一个字符的编码值
System.out.println(new String(ch)); //将全部字符数组转化为字符串
System.out.println(new String(ch,1,2)); //将部分字符数组转化为字符串
}
}
执行结果:
D:\360downloads\bin\java.exe "-javaagent:D:\360downloads\IntelliJ IDEA Community Edition 2020.3.2\lib\idea_rt.jar=50971:D:\360downloads\IntelliJ IDEA Community Edition 2020.3.2\bin" -Dfile.encoding=UTF-8 -classpath D:\java程序\out\production\java程序 com.company.Test
HELLO
EL
Process finished with exit code 0
范例3-47判断一个字符串是否完全由数字组成
public class Test {
public static void main(String[] args){
String str="123hello";
System.out.println(isNum(str));
}
public static boolean isNum(String temp){
char ch[]=temp.toCharArray(); //将字符串转化为字符数组
int i;
for(i=0;i<ch.length;i++){
if (ch[i] < '0' || ch[i]>'9') { //如果有一个不是数字,返回false
return false;
}
}
return true;
}
}
执行结果:
D:\360downloads\bin\java.exe "-javaagent:D:\360downloads\IntelliJ IDEA Community Edition 2020.3.2\lib\idea_rt.jar=51225:D:\360downloads\IntelliJ IDEA Community Edition 2020.3.2\bin" -Dfile.encoding=UTF-8 -classpath D:\java程序\out\production\java程序 com.company.Test
false
Process finished with exit code 0
3.9.2 字节与字符串
字节使用byte描述,字节一般用于数据的传输或编码的转换,而在String类里面提供了将字符串转变为字节数组的操作,就是为了传输以及编码的转换。
范例3-48 观察字符串与字节数组的转换
public class Test {
public static void main(String[] args){
String str="hello";
byte data[]=str.getBytes();
int i;
for(i=0;i<data.length;i++)
data[i]-=32;
System.out.println(new String (data));
}
}
执行结果:
D:\360downloads\bin\java.exe "-javaagent:D:\360downloads\IntelliJ IDEA Community Edition 2020.3.2\lib\idea_rt.jar=51606:D:\360downloads\IntelliJ IDEA Community Edition 2020.3.2\bin" -Dfile.encoding=UTF-8 -clas
spath D:\java程序\out\production\java程序 com.company.Test
HELLO
Process finished with exit code 0
3.9.3字符串的比较2
如果要进行字符串内容相等的判断需要使用equals()方法,而在String类中针对字符串内容的比较方法也提供了多种。
equals()方法与equalsIgnoreCase()方法的对比 (equals()方法和equalsIgnoreCase()方法的返回值类型都是boolean型
范例·3-49 相等判断
public class Test {
public static void main(String[] args){
String stra="hello";
String strb="Hello";
System.out.println(stra.equals(strb));
System.out.println(stra.equalsIgnoreCase(strb));
}
}
执行结果:
D:\360downloads\bin\java.exe "-javaagent:D:\360downloads\IntelliJ IDEA Community Edition 2020.3.2\lib\idea_rt.jar=54485:D:\360downloads\IntelliJ IDEA Community Edition 2020.3.2\bin" -Dfile.encoding=UTF-8 -classpath D:\java程序\out\production\java程序 com.company.Test
false 说明equals()方法在比较字符串内容时是区分字母大小写的
true 说明equalsIgnoreCase()方法在比较字符串内容时是不区分大小写的
Process finished with exit code 0
equals() 方法和equalsIgnoreCase()方法都是只能判断两个字符串内容是否相同,但如果需要比较字符串的大小关系,就必须使用compareTo()方法来完成。
compareTo()方法:
1、返回值类型为int型,
2、当前者字符串大于后者字符串时,返回值大于0,
3、小于,返回值小于0;
4、等于,返回值等于0;
范例3-50 观察compareTo()方法的使用
public class Test {
public static void main(String[] args){
String stra="hello";
String strb="hEllo";
System.out.println(stra.compareTo(strb));
System.out.println(stra.compareTo(stra));
System.out.println(strb.compareTo((stra)));
}
}
执行结果:
D:\360downloads\bin\java.exe "-javaagent:D:\360downloads\IntelliJ IDEA Community Edition 2020.3.2\lib\idea_rt.jar=54545:D:\360downloads\IntelliJ IDEA Community Edition 2020.3.2\bin" -Dfile.encoding=UTF-8 -classpath D:\java程序\out\production\java程序 com.company.Test
32 (e的编码值大于E的编码值32)
0
-32
Process finished with exit code 0
3.9.4字符串的查找
如果要从一个完整的字符串中查找或判断某一个字符串是否存在,可以用字符串查找的方法,
范例3-51使用contains()方法判断某一个字符串是否存在
public class Test {
public static void main(String[] args){
String str="helloworld";
System.out.println(str.contains("world"));
}
}
执行结果:
D:\360downloads\bin\java.exe "-javaagent:D:\360downloads\IntelliJ IDEA Community Edition 2020.3.2\lib\idea_rt.jar=54596:D:\360downloads\IntelliJ IDEA Community Edition 2020.3.2\bin" -Dfile.encoding=UTF-8 -classpath D:\java程序\out\production\java程序 com.company.Test
true(说明在str中存在world这个字符串)
Process finished with exit code 0
范例3-51 使用indexOf()方法查找
public class Test {
public static void main(String[] args) {
String str = "helloworld"; //实例化字符串对象
System.out.println(str.indexOf("world")); //返回满足条件字符串的第一个字母的索引
System.out.println(str.indexOf("l")); //返回的是第一个查找到字符串"l"的位置 (从前向后查找)
System.out.println(str.indexOf("l", 5)); //从第6个字母开始查找"l"的位置(这里注意,字符串的索引是从0开始的)
System.out.println(str.lastIndexOf("l")); //从后向前开始查找"l"的位置
}
}
执行结果:
D:\360downloads\bin\java.exe "-javaagent:D:\360downloads\IntelliJ IDEA Community Edition 2020.3.2\lib\idea_rt.jar=54638:D:\360downloads\IntelliJ IDEA Community Edition 2020.3.2\bin" -Dfile.encoding=UTF-8 -classpath D:\java程序\out\production\java程序 com.company.Test
5
2
8
8
Process finished with exit code 0
利用indexOf()方法查找指定字符串的位置时,是默认从前向后查找的,但是也可以利用indexOf()方法的重载从指定位置开始查找,或者利用lastindexOf()方法从后向前查找
这里还可以使用indexOf()方法来判断某一个字符串是否存在,即如果返回值为-1即没有这个字符串,但开发中一般都直接使用contains()方法来进行判断。
范例3-54开头或结尾判断
范例3-54开头或结尾判断
public class Test {
public static void main(String[] args) {
String str = "##helloworld**"; //实例化字符串对象
System.out.println(str.startsWith("##")); //判断是否以"##"开头
System.out.println(str.endsWith("**")); //判断是否以"**" 结尾
}
}
执行结果:
D:\360downloads\bin\java.exe "-javaagent:D:\360downloads\IntelliJ IDEA Community Edition 2020.3.2\lib\idea_rt.jar=54702:D:\360downloads\IntelliJ IDEA Community Edition 2020.3.2\bin" -Dfile.encoding=UTF-8 -classpath D:\java程序\out\production\java程序 com.company.Test
true
true
Process finished with exit code 0
3.9.5字符串的替换
在String类中提供了字符串的替换操作,即可以将特定的字符串内容进行整体替换。
注释: String regex是要被替换掉的内容,而String replacement是要用来替换的新内容
replaceAll()方法是替换掉所有满足条件的内容,即是将所有指定的字符串给替换掉
replaceFirst()方法是替换掉第一个满足条件的内容,
两个方法返回值类型都是String类
范例 3-55观察替换的结果
public class Test {
public static void main(String[] args) {
String str="helloworld";
String resultA=str.replaceAll("l","_"); //替换所有的l
String resultB=str.replaceFirst("l","_"); //替换第一个l
System.out.println(resultA);
System.out.println(resultB);
}
}
执行结果:
D:\360downloads\bin\java.exe "-javaagent:D:\360downloads\IntelliJ IDEA Community Edition 2020.3.2\lib\idea_rt.jar=54778:D:\360downloads\IntelliJ IDEA Community Edition 2020.3.2\bin" -Dfile.encoding=UTF-8 -classpath D:\java程序\out\production\java程序 com.company.Test
he__owor_d
he_loworld
Process finished with exit code 0
3.9.6字符串的截取
从一个字符串中,可以取出指定的字符串,称为字符串的截取。、
注释:
1、substring()方法有方法重载,其中一种是指定的索引开始截取到结尾
2、第二种是在指定的区间内进行截取,
3、关于截取的参数设置:substring()方法接收的参数只能是正整数,还要特别注意,字符串的索引是从0开始的
范例3-56 验证字符串截取的操作
public class Test {
public static void main(String[] args) {
String str="helloworld";
String resultA=str.substring(5); //从第6个字符开始截取
String resultB=str.substring(0,5); //从第一个字符截取到第6个字符
System.out.println(resultA);
System.out.println(resultB);
}
}
执行结果:
D:\360downloads\bin\java.exe "-javaagent:D:\360downloads\IntelliJ IDEA Community Edition 2020.3.2\lib\idea_rt.jar=54830:D:\360downloads\IntelliJ IDEA Community Edition 2020.3.2\bin" -Dfile.encoding=UTF-8 -classpath D:\java程序\out\production\java程序 com.company.Test
world
hello
Process finished with exit code 0
3.9.7字符串的拆分
所谓的拆分操作就是按照一个指定的字符串标记,将一个完整的字符串分解为字符串数组。
范例3-57 进行全部拆分
public class Test {
public static void main(String[] args) {
String str="hello world i am chenfucheng";
String temp []=str.split(" "); //以空格为分割符来拆分str这个字符串,并将拆分的内容保存到字符串数组中
int i;
for(i=0;i<temp.length;i++)
System.out.println(temp[i]);
}
}
执行结果:
D:\360downloads\bin\java.exe "-javaagent:D:\360downloads\IntelliJ IDEA Community Edition 2020.3.2\lib\idea_rt.jar=54866:D:\360downloads\IntelliJ IDEA Community Edition 2020.3.2\bin" -Dfile.encoding=UTF-8 -classpath D:\java程序\out\production\java程序 com.company.Test
hello
world
i 拆分内容保存在String类的对象数组(字符串数组)中,
am
chenfucheng
Process finished with exit code 0
提示:如果使用空字符串为拆分标志则表示根据每个字符拆分
在进行字符串的拆分时,如果spilt()方法中设置的是空字符串(空字符串是长度为0的字符串"",而不是null,否则会出现空指向问题),就表示全部拆分,即将一个字符串变为一个字符数组,而数组的长度就是字符串的长度。
public class Test {
public static void main(String[] args) {
String str="hello world";
String temp []=str.split(""); //以空格为分割符来拆分str这个字符串,并将拆分的内容保存到字符串数组中
int i;
for(i=0;i<temp.length;i++)
System.out.print(temp[i]+"、");
}
}
执行结果:
D:\360downloads\bin\java.exe "-javaagent:D:\360downloads\IntelliJ IDEA Community Edition 2020.3.2\lib\idea_rt.jar=55044:D:\360downloads\IntelliJ IDEA Community Edition 2020.3.2\bin" -Dfile.encoding=UTF-8 -classpath D:\java程序\out\production\java程序 com.company.Test
h、e、l、l、o、 、w、o、r、l、d、
Process finished with exit code 0
范例3-59 拆分为指定的个数
public class Test {
public static void main(String[] args) {
String str="hello world chen";
String temp []=str.split(" ",2); //以空格为分割符来拆分str这个字符串,并将拆分的内容保存到字符串数组中,拆分为2部分
int i;
for(i=0;i<temp.length;i++)
System.out.println(temp[i]);
}
}
执行结果:
D:\360downloads\bin\java.exe "-java Community Edition 2020.3.2\bin" -Dfile.encoding=UTF-8 -classpath D:\java程序\out\production\java程序 com.company.Test
hello
world chen
Process finished with exit code 0
分析:本程序在进行拆分是设置了拆分的个数,所以只将全部内容拆分为了长度为2的字符串对象数组。
注意:要避免正则表达式的影响,可以进行转义操作
实际上split()方法的字符串拆分能否正常进行,与正则表达式的操作有关·,所以有些时候会出现无法拆分的情况。例如:给出一个IP地址(192.168.1.2),那么拆分的时候肯定是根据“.”来进行拆分,但如果直接使用 “.” 是不可能正常拆分的,
范例 3-60 错误的拆分操作
public class Test {
public static void main(String[] args) {
String IP=“192.168.1.2”;
String temp[]=IP.split(".");
int i;
for(i=0;i< temp.length;i++)
System.out.println(temp[i]);
}
}
执行结果:
没有输出
此时操作不能正常进行,而想要正常进行,就必须要对拆分标志 “.” 进行转义,在Java中转义需要使用“\”(\表示一个)
范例 3-61正常的拆分操作
public class Test {
public static void main(String[] args) {
String IP="192.168.1.2";
String temp[]=IP.split("\\."); //对拆分标志”.“进行转义
int i;
for(i=0;i< temp.length;i++)
System.out.println(temp[i]);
}
}
执行结果:
D:\360downloads\bin\java.exe "-javaagent:D:\360downloads\IntelliJ IDEA Community Edition 2020.3.2\lib\idea_rt.jar=55130:D:\360downloads\IntelliJ IDEA Community Edition 2020.3.2\bin" -Dfile.encoding=UTF-8 -classpath D:\java程序\out\production\java程序 com.company.Test
192
168
1
2
Process finished with exit code 0
在实际开发中,拆分是非常常见的操作,常常要对接收到的数据进行拆分,并且对于有些数据,在进行一次拆分之后还并不能满足要求,需要多次拆分。
例如:对 张三:20|李四:30|王五:40进行拆分
范例3-63 复杂的拆分操作
public static void main(String[] args) {
String str = "张三:20|李四:30|王五:40";
String temp1[] = str.split("\\|"); //首先利用“|”进行拆分,不过这里要进行转义
int i;
for (i = 0; i < temp1.length; i++) {
String temp2 []=temp1[i].split(":",2);//再次拆分,这里要注意拆分标志是英文的还是中文的,因为中英文中的一些符号是不的
System.out.println("姓名:"+temp2[0]+" 年龄:"+temp2[1]);
}
}
}
执行结果:
D:\360downloads\bin\java.exe "-javaagent:D:\360downloads\IntelliJ IDEA Community Edition 2020.3.2\lib\idea_rt.jar=55268:D:\360downloads\IntelliJ IDEA Community Edition 2020.3.2\bin" -Dfile.encoding=UTF-8 -classpath D:\java程序\out\production\java程序 com.company.Test
姓名:张三 年龄:20
姓名:李四 年龄:30
姓名:王五 年龄:40
Process finished with exit code 0
3.9.8 其他方法
1、字符串连接操作可以使用contact()方法,但是开发中大部分情况都是直接使用 + 号来连接字符串的。
范例3-64 字符串连接
public class Test {
public static void main(String[] args) {
String str="hello";
str=str.concat("world"); //使用concat()方法连接字符串
System.out.println(str);
str+="!!"; //使用+号来连接字符串
System.out.println(str);
}
}
执行结果:
D:\360downloads\bin\java.exe "-javaagent:D:\360downloads\IntelliJ IDEA Community Edition 2020.3.2\lib\idea_rt.jar=55325:D:\360downloads\IntelliJ IDEA Community Edition 2020.3.2\bin" -Dfile.encoding=UTF-8 -classpath D:\java程序\out\production\java程序 com.company.Test
helloworld
helloworld!!
Process finished with exit code 0
范例3-64 转小写与大写操作
public class Test {
public static void main(String[] args) {
String str="**(hEllo*%";
System.out.println(str.toUpperCase()); //转大写
System.out.println(str.toLowerCase()); //转小写
}
}
执行结果:
D:\360downloads\bin\java.exe "-javaagent:D:\360downloads\IntelliJ IDEA Community Edition 2020.3.2\lib\idea_rt.jar=55344:D:\360downloads\IntelliJ IDEA Community Edition 2020.3.2\bin" -Dfile.encoding=UTF-8 -classpath D:\java程序\out\production\java程序 com.company.Test
**(HELLO*%
**(hello*%
Process finished with exit code 0
分析:在String类中提供的toUpperCase()方法和toLowerCase()方法,在转换时只会将字母的大小写进行转换。
范例3-65 去掉左右空格
public class Test {
public static void main(String[] args) {
String str=" hello world ";
System.out.println("{"+str+"}"); //输出str,{}来显示左右空格
System.out.println("{"+str.trim()+"}"); //输出清除了左右空格之后的str
}
}
执行结果:
D:\360downloads\bin\java.exe "-javaagent:D:\360downloads\IntelliJ IDEA Community Edition 2020.3.2\lib\idea_rt.jar=63127:D:\360downloads\IntelliJ IDEA Community Edition 2020.3.2\bin" -Dfile.encoding=UTF-8 -classpath D:\java程序\out\production\java程序 com.company.Test
{ hello world }
{hello world}
Process finished with exit code 0
分析:
trim()方法只能去除字符串左右的空格,而不能清除字符串内空格
提示:可以用replaceAll()方法来清楚所有空格,只需要用空字符串替换掉所有的空格即可,
例如:str.replaceAll(" “,”");
范例3-67取得字符串长度
public class Test {
public static void main(String[] args) {
String str = "hello";
System.out.println(str.length()); //用.length()方法来取得字符串的长度
}
}
执行结果;
D:\360downloads\bin\java.exe "-javaagent:D:\360downloads\IntelliJ IDEA Community Edition 2020.3.2\lib\idea_rt.jar=63205:D:\360downloads\IntelliJ IDEA Community Edition 2020.3.2\bin" -Dfile.encoding=UTF-8 -classpath D:\java程序\out\production\java程序 com.company.Test
5
Process finished with exit code 0
提示:关于length的说明:
String类中的length()方法和数组中的 length 属性是不同的,
String类中的取得长度使用的是length()方法
而数组中取得长度使用的是length属性/
范例3-68 判断是否为空字符串(如果觉得isEmpty()不方便,可以用"".equals(str)来判断)
public class Test {
public static void main(String[] args) {
String str = "hello";
System.out.println(str.isEmpty()); //判断str是否是空字符串
System.out.println("".isEmpty()); //判断""是否是空字符串(是)
}
}
执行结果:
D:\360downloads\bin\java.exe "-javaagent:D:\360downloads\IntelliJ IDEA Community Edition 2020.3.2\lib\idea_rt.jar=63225:D:\360downloads\IntelliJ IDEA Community Edition 2020.3.2\bin" -Dfile.encoding=UTF-8 -classpath D:\java程序\out\production\java程序 com.company.Test
false
true
Process finished with exit code 0
范例 3-69将首字母大写 (这一操作很重要)
public class Test {
public static void main(String[] args) {
String str = "hello";
System.out.println(initcap(str));
}
/**
* 实现首字母大写
* @param str1
* @return
*/
public static String initcap(String str1){
String temp=str1.substring(0,1); //先利用substring()方法将首字母提取出来,
return temp.toUpperCase()+str1.substring(1); //再利用toUpperCase()方法将首字母大写连接剩余字符串
}
}
执行结果:
D:\360downloads\bin\java.exe "-javaagent:D:\360downloads\IntelliJ IDEA Community Edition 2020.3.2\lib\idea_rt.jar=63270:D:\360downloads\IntelliJ IDEA Community Edition 2020.3.2\bin" -Dfile.encoding=UTF-8 -classpath D:\java程序\out\production\java程序 com.company.Test
Hello
Process finished with exit code 0
3.10 this关键字
在Java中,关键字this有三个功能:
1、调用本类属性
2、调用本类方法
3、表示当前对象
3.10.1 调用本类属性
在一个类的定义方法中可以直接访问类中的属性,但是很多时候有可能会出现方法参数名称和属性名称重复的情况,这是就要用到 "this.属性"的形式来明确调用的是类中的属性而不是方法中的参数。
范例3-70观察方法参数名称和属性名称相同时未用this
class Book{
private String name;
private double price;
public Book1(String name,double price)
{
name=name;
price=price;
}
可以看到,当前Book类中有name和price两个属性,构造方法中有两个参数的名称和属性名称相同,
此时在主类中实例化Book类的对象时是不能正常调用该构造方法来进行初始化属性内容的。
原因:因为在Java中的变量使用具备“就进取用”的原则,在构造方法中已经存在name和price变量名称,所以如果直接调用name和price变量将不会使用类中的属性,而只会使用该构造方法中的参数。
在这种情况下为了可以明确地找到要访问的变量属于类中的属性,就需要在变量前加上关键字this.,这样才可以准确的进行属性的标记。
注意:
为了减少不必要的麻烦,在类中使用本类中的属性时,都最好使用关键字this
范例3-71使用关键字this来明确地访问类中地属性
class Book{
private String name;
private double price;
public Book1(String name,double price)
{
this.name=name; //使用关键字this来表示使用的是本类属性,这样即使方法中的参数与属性名称相同也可以准确定位
this.price=price;
}
3.10.2 调用本类方法 (这也是在定义类时防止大量重发代码的有效操作)
this本质就是明确进行本类结构的标记。而除了访问类中的属性外,还可以访问类中的方法
调用本类方法分为两种形式:
1、普通方法:如果要强调调用的是本类方法,可以使用 this.方法();
2、构造方法:在一个构造方法中调用其他构造,可以使用this();来调用
范例3-72 调用本类普通方法
class Book1{
private String name;
private double price;
public Book1(String name,double price)
{
this.name=name;
this.price=price;
}
public void print(){
System.out.println("我是你爸爸");
}
public String getInfo(){
this.print(); //调用本类方法
return "书名:"+name+",价格:"+price;
}
}
public class Main {
public static void main(String[] args) {
Book1 book1=new Book1("java开发",89.0); //匿名对象实例化并调用类中方法
System.out.println(book1.getInfo());
}
}
执行结果:
D:\360downloads\bin\java.exe "-javaagent:D:\360downloads\IntelliJ IDEA Community Edition 2020.3.2\lib\idea_rt.jar=63535:D:\360downloads\IntelliJ IDEA Community Edition 2020.3.2\bin" -Dfile.encoding=UTF-8 -classpath D:\java程序\out\production\java程序 com.company.Main
我是你爸爸
书名:java开发,价格:89.0
Process finished with exit code 0
分析: 在getInfo()方法取得对象属性内容前,先调用了本类方法print(),利用了this.方法()的形式;
在定义构造方法中,可能会有大量重发代码的出现,为了避免这种情况,利用this()来调用本类中的构造方法就是一个很好的途径。
例如:不管是多少个参数的构造方法,都要求在实例化对象时输出大量的提示信息,
范例3-73 观察问题
class Book1{
private String name;
private double price;
public Book1(){
System.out.println("这是提示信息"); //假设这是一大串的代码
}
public Book1(String str){
System.out.println("这是提示信息"); //这里和第一个构造方法中的提示信息相同,且很长的代码
this.name=str;
}
public Book1(String name,double price)
{
System.out.println("这是提示信息");
this.name=name;
this.price=price;
}
public String getInfo(){
return "书名:"+name+",价格:"+price;
}
}
问题:
这里的每个构造方法都有一段提示信息的代码,造成大量重复代码,这时就需要用到调用本类中的调用方法。
范例3-74 使用this()调用本类中构造方法来解决大量重复代码问题
class Book1{
private String name;
private double price;
public Book1(){
System.out.println("这是提示信息"); //假设这是一大串的代码
}
public Book1(String str){
this(); //调用本类中的无参构造来代替提示信息的代码(因为无参构造中已经包含了)
this.name=str;
}
public Book1(String name,double price)
{
this(name); //调用1参构造方法来代替提示信息的代码,同时减少了为names属性赋值的代码
this.price=price;
}
public String getInfo(){
return "书名:"+name+",价格:"+price;
}
}
public class Main {
public static void main(String[] args) {
Book1 book1=new Book1("java开发",89.0); //在实例化的同时调用了双参构造方法
System.out.println(book1.getInfo());
}
}
执行结果:
D:\360downloads\bin\java.exe "-javaagent:D:\360downloads\IntelliJ IDEA Community Edition 2020.3.2\lib\idea_rt.jar=63636:D:\360downloads\IntelliJ IDEA Community Edition 2020.3.2\bin" -Dfile.encoding=UTF-8 -classpath D:\java程序\out\production\java程序 com.company.Main
这是提示信息 说明双参构造成功调用了1参构造
书名:java开发,价格:89.0 (可以看到这样的方法是可以通过的)
Process finished with exit code 0
注意:关于this的使用限制:
在使用this()来调用本类中的构造方法时有两个重要的限制
1、使用this()调用构造方法形式的代码只能放在构造方法的第一行
2、进行构造方法的互相调用时,必须保留调用的出口
即不能出现调用死循环,例如: 无参构造调用双参构造,双参构造调用1参构造,1参构造又调用无参构造。这就构成了调用死循环,编译时会出现构造方法递归调用的提示。
利用构造方法来简化代码的例子:
程序1:
class Emp{
private String empno;
private String ename;
private String job;
private double sal;
//private double comm;
public Emp(){
this.empno="0";
this.ename="无名氏";
this.job="无职务";
this.sal=0;
}
public Emp(String empno){
this.empno=empno;
this.ename="张三";
this.job="临时工";
this.sal=200;
}
public Emp(String empno,String ename){
this.empno=empno;
this.ename=ename;
this.job="经理";
this.sal=1000;
}
public Emp(String str1,String str2,String str3,double sal1){
empno=str1;
ename=str2;
job=str3;
sal=sal1;
}
}
可以看到,这个程序中出现了大量的重复代码,
思考:如果在前几个构造方法中直接调用4个参数的调用方法,只要在参数框内输入数据就可以避免上述大量的代码重复问题
范例:利用构造方法简化程序
class Emp{
private String empno;
private String ename;
private String job;
private double sal;
//private double comm;
public Emp(){
this("0","无名氏","无职务",0); //调用4参构造
}
public Emp(String empno){
this(empno,"张三","临时工",20); /调用4参构造
}
public Emp(String empno,String ename){
this(empno,ename,"经理",100);
}
public Emp(String str1,String str2,String str3,double sal1){
empno=str1;
ename=str2;
job=str3;
sal=sal1;
}
分析:很明显代码简洁成都大幅度提升,所以要灵活使用this来调用本类方法来简化代码。
3.10.3 表示当前对象
'this关键字在应用的过程中有一个最为重要的概念——当前对象。当前对象就是指当前正在调用类中方法的实例化对象。
范例3-78直接输出对象,验证this表示的当前对象
public class Main {
public static void main(String[] args) {
Book bk=new Book(); //实例化Bool类对象
System.out.println("bk="+bk); //主方法中输出Book类对象
bk.print(); //调用Book类对象的print()方法输出Book类对象,此时bk为当前对象
System.out.println("********");
Book bk2=new Book();
System.out.println("bk2="+bk2); //主方法中输出Book类对象
bk.print(); //调用Book类对象的print()方法输出Book类对象,此时bk2为当前对象
}
}
执行结果:
D:\360downloads\bin\java.exe "-javaagent:D:\360downloads\IntelliJ IDEA Community Edition 2020.3.2\lib\idea_rt.jar=63819:D:\360downloads\IntelliJ IDEA Community Edition 2020.3.2\bin" -Dfile.encoding=UTF-8 -classpath D:\java程序\out\production\java程序 com.company.Main
bk=com.company.Book@7b23ec81
this=com.company.Book@7b23ec81 说明this表示的是当前对象bk
********
bk2=com.company.Book@6acbcfc0
this=com.company.Book@7b23ec81 说明在不同的对象调用同一方法时,当前对象会发生变化
Process finished with exit code 0
3.11引用传递
引用传递是整个Java的精髓存在,引用传递的核心意义:同一块堆内存空间可以被不同的栈内存指向,不同的栈内存可以对同一块堆内存空间的内容进行修改。
范例3-77第一道引用传递范例
package com.company;
class Message{
private int num=10;
/**
* 本类中没有提供无参构造方法,而是提供有参构造,可以接收num的内容
* @param num 接收num属性地内容
*/
public Message(int num){
this.num=num; //为num赋值
}
public void setNum(int num) {
this.num = num;
}
public int getNum() {
return num;
}
}
public class Main {
public static void main(String[] args) {
Message msg=new Message(30);
fun(msg);
System.out.println(msg.getNum());
}
public static Message fun(Message temp){ //fun()方法的参数是Message类型,接收了msg的引用,temp和msg指向同一块堆内存
temp.setNum(100); //修改num属性内容
return temp;
}
}
执行结果:
D:\360downloads\bin\java.exe "-javaagent:D:\360downloads\IntelliJ IDEA Community Edition 2020.3.2\lib\idea_rt.jar=64171:D:\360downloads\IntelliJ IDEA Community Edition 2020.3.2\bin" -Dfile.encoding=UTF-8 -classpath D:\java程序\out\production\java程序 com.company.Main
100
Process finished with exit code 0
分析:fun()方法的参数接收了Message类对象的引用,temp和msg指向同一块堆内存,以便fun()方法中可以修改msg对象里的属性内容,而当fun()方法执行完毕后,temp就会断开与堆内存的引用,但是堆内存空间里的内容已经修改并保存下来了。
这是一道标准的引用传递范例,但是在考虑引用传递时,就不能忽略一种特殊的类——String类。
范例3-78 第二道引用传递 (String类的引用传递)
public class Main {
public static void main(String[] args) {
String msg="hello";
fun(msg); //引用传递
System.out.println(msg);
}
public static void fun(String temp){ //接收引用传递
temp="world"; //改变字符串引用
}
}
结果:
D:\360downloads\bin\java.exe "-javaagent:D:\360downloads\IntelliJ IDEA Community Edition 2020.3.2\lib\idea_rt.jar=52826:D:\360downloads\IntelliJ IDEA Community Edition 2020.3.2\bin" -Dfile.encoding=UTF-8 -classpath D:\java程序\out\production\java程序 com.company.Main
hello
Process finished with exit code 0
分析:输出的msg依旧是hello,说明msg指向的堆内存空间的内容并没有改变,这是因为虽然fun()方法的参数接收了String类的引用传递,但是由于在fun()方法中修改字符串temp的内容时实际上是修改了temp的引用关系,此时temp和msg指向的不是同一块堆内存空间,改变的只是temp的引用关系(指向了另一块堆内存),而没有修改msg指向的堆内存空间的内容。
关键:(字符串内容一旦定义则无法改变)在fun方法中修改temp字符串的内容时实际上是修改了temp的引用·关系,此时断开了temp与(msg指向的)堆内存空间的引用关系,故不会修改msg指向的堆内存空间里的内容。(而fun方法执行完毕后,temp将会失效,其更改后指向的那块堆内存也将成为垃圾)
提示:基本数据类型在进行参数传递时使用的是值传递,所以在fun方法中做的任何修改都不会影响主方法中基本数据类型参数的值。
既然引用·传递时传递普通类时可以对对象的内容进行修改,而传递的是String类时由于其特殊性而导致不能直接修改内容,不妨将Sting类的对象设置为普通类的一个属性,这样就可以修改在引用传递中修改String 类的内容了。
范例3-79 第三道引用传递,将普通类和String类结合起来的引用传递
package com.company;
class Message{
private String str="此内容无用"; //定义String类属性并设置内容
public Message(String str){
this.str=str;
}
public void setStr(String str) {
this.str = str;
}
public String getStr() {
return str;
}
}
public class Main {
public static void main(String[] args) {
Message msg=new Message("hello"); //实例化Message类对象并初始化
fun(msg); //引用传递
System.out.println(msg.getStr());
}
public static void fun(Message temp){ //接收Message类对象引用传递
temp.setStr("world"); //修改str属性内容
}
}
执行结果:
D:\360downloads\bin\java.exe "-javaagent:D:\360downloads\IntelliJ IDEA Community Edition 2020.3.2\lib\idea_rt.jar=52928:D:\360downloads\IntelliJ IDEA Community Edition 2020.3.2\bin" -Dfile.encoding=UTF-8 -classpath D:\java程序\out\production\java程序 com.company.Main
World (说明成功修改了str属性的内容)
Process finished with exit code 0
分析:fun()方法的参数接收了Message类的对象引用传递,此时temp和msg指向同一块堆内存空间,虽然str是String类型的,但是它是作为属性定义在了Message类中,temp和msg都指向了保存属性str的那块堆内存,所以即使是在fun方法中改变了str的引用关系,但是由于temp和msg一直都指向了str,所以这样的修改时可以在主方法中保存下来的。
3.11.2 引用传递的实际引用。
面向对象是一种可以抽象化描述现实社会事物的编程思想。
第一个面向对象学习之中就是以数据表简单Java类的转换操作作为终点,也是下一个起点。
下面实现这样一种类的设计:每一个人都有一辆汽车或者·没有汽车。(一对一映射)
很明显,人是一个类,汽车也是一个类,人应该包含一个车的属性(来描述人是否有车),同样的,车应该有一个 人类型的属性(来描述车属于某一个人),这样就可以得到两者间的关系表。
范例3-82 代码实现 每一个人都有一辆汽车或者没有汽车的数据 关系 (这里省略了setter和getter方法的构造)
package com.company;
class Member{
private int mid; //人的编号
private String name;
private Car car; //一个属于Car类型的属性,如果没有车则属性内容为null
public Member(int mid,String name){
this.mid=mid; //设置属性内容
this.name=name;
}
public String getInfo(){
return "人员编号:"+mid+",人员姓名:"+name; //用来输出人的独立信息
}
public void setCar(Car car) {
this.car = car;
}
public Car getCar(){
return this.car;
}
}
class Car{
private int pmid; //车的编号
private String pname;
private Member member; //车中一个书Member类型的属性,如果该车没有主人,则该内容为null
public Car(int pmid,String pname){
this.pmid=pmid;
this.pname=pname;
}
public void setMember(Member member) {
this.member = member;
}
public Member getMember() {
return member;
}
public String getInfo(){
return "车牌号:"+pmid+",车名:"+pname; //用来输出车的信息(独立信息
}
}
以上实现了每一个人都有一辆汽车或者没有汽车的关系 的类的关联定义
下面来测试程序,分为两步:
1、根据定义的结构关系来设置数据
2、根据定义的结构关系来输出数据
测试代码如下:
publicclassTest{
publicstaticvoidmain(String[]args){
Memberm=newMember(110,"张三");//独立对象
Carc=newCar(250,"法拉利");//独立对象
m.setCar(c);//一个人有一辆车
c.setMember(m);//一辆车属于一个人\
System.out.println(c.getMember().getInfo());//通过车找到人
System.out.println(m.getCar().getInfo());//通过人找到车
}
}
执行结果:
D:\360downloads\bin\java.exe "-javaagent:D:\360downloads\IntelliJ IDEA Community Edition 2020.3.2\lib\idea_rt.jar=53840:D:\360downloads\IntelliJ IDEA Community Edition 2020.3.2\bin" -Dfile.encoding=UTF-8 -classpath D:\java程序\out\production\java程序 com.company.Test
人员编号:110,人员姓名:张三 (通过车找到了人
车牌号:250,车名:法拉利 (通过人找到了车
Process finished with exit code 0
分析:说明利用类的引用关系成功将人与汽车之间的关系通过代码呈现了出来,
下面又有一个问题,如果每一个人都有孩子或者没有,而孩子又都有一辆车或者没有车,又该如何设置关系呢?
两种方法:
1、设置孩子的类,在通过类的引用来建立其孩子和父母之间、孩子和车之间的关系,但这种方法有很大的弊端,就是这样会重复设置引用关系,不仅加大代码负担,更麻烦的是当孩子还有孩子时就会导致引用关系十分的复杂,所以这种方式不可取
2、换个角度思考,孩子也是人,所以可以在Member类种设置一个Member类型的孩子属性,这样就可以实现类的嘀咕,不仅减少了代码,更重要的是这样使得引用关系清晰明了。
代码实现如下(一个人有孩子,孩子还有车)
package com.company;
class Member{
private Member child; //一个属于Member类型地属性表示孩子,如果没有则内容为null
private int mid; //人的编号
private String name;
private Car car; //一个属于Car类型的属性,如果没有车则属性内容为null
public Member(int mid,String name){
this.mid=mid; //设置属性内容
this.name=name;
}
public String getInfo(){
return "人员编号:"+mid+",人员姓名:"+name; //用来输出人的独立信息
}
public void setChild(Member child) { //设置child属性
this.child = child;
}
public void setCar(Car car) { //设置Car的属性
this.car = car;
}
public Member getChild() { //返回地是Member类型地对象
return child;
}
public Car getCar(){ //返回地是Car类型地对象
return this.car;
}
}
class Car{
private int pmid; //车的编号
private String pname;
private Member member; //车中一个书Member类型的属性,如果该车没有主人,则该内容为null
public Car(int pmid,String pname){
this.pmid=pmid;
this.pname=pname;
}
public void setMember(Member member) {
this.member = member;
}
public Member getMember() {
return member;
}
public String getInfo(){
return "车牌号:"+pmid+",车名:"+pname; //用来输出车的信息(独立信息
}
}
测试代码如下:
public class Test {
public static void main(String[] args) {
Member m=new Member(110,"张三"); //独立对象
Member chl=new Member(111,"张武");
Car c=new Car(250,"法拉利"); //独立对象
Car cc=new Car(251,"兰博基尼");
m.setCar(c); //一个人有一辆车
c.setMember(m);//一辆车属于一个人\
m.setChild(chl); //这个孩子属于张三
cc.setMember(chl); //这辆兰博基尼属于这个孩子
chl.setCar(cc); //这个孩子有一辆车
System.out.println(c.getMember().getInfo()); //通过车找到人
System.out.println(m.getCar().getInfo()); //通过人找到车
System.out.println("*****");
System.out.println(m.getChild().getInfo()); //通过人找孩子
System.out.println(m.getChild().getCar().getInfo()); 通过人找孩子地车
}
}
执行结果:
D:\360downloads\bin\java.exe "-javaagent:D:\360downloads\IntelliJ IDEA Community Edition 2020.3.2\lib\idea_rt.jar=54614:D:\360downloads\IntelliJ IDEA Community Edition 2020.3.2\bin" -Dfile.encoding=UTF-8 -classpath D:\java程序\out\production\java程序 com.company.Test
人员编号:110,人员姓名:张三
车牌号:250,车名:法拉利
*****
人员编号:111,人员姓名:张武
车牌号:251,车名:兰博基尼
Process finished with exit code 0
技术穿越:
1、这类引用设计思路是现实生活中非常常见的类型,在以后的开发中也会使用到。
2、另一种设计模式:合成设计模式:先通过设计各个部分的类,最后将所有的类集合到一个类中作为属性,这种设计模式称为合成设计模式。
例如:电脑由主机、显示屏、鼠标、键盘、等组成,主机又由cpu、内存、主板、硬盘等组成,则就可以如下虚拟代码设计:
Class 键盘{};
Class鼠标{};
Class cpu{};
Class 硬盘{};
Class 内存{};
Class 主板{};
Class 主机{
private cpu 对象;
private 内存 对象数组[];
private 硬盘 对象;
private 主板 对象;
}
Class 电脑{
private 主机 对象;
private 显示屏 对象;
private 鼠标;
private 键盘;
}
3.12 数据表与简单Java·类的映射
简单Java类在实际开发中都是根据数据表的定义来实现的。
用简单Java类实现数据表的一般步骤:
第一步:基本字段的转换:类名称=表名称,属性名称=表字段;
第二步:,设置外键,即设置关联字段(引用)
第三步:进行代码测试:
1、根据结构关系设置数据
a、设置独立对象
b、设置关系(即通过类中的setter方法来进行引用关系的设置)
2、根据结构关系取出数据
下面我们来实现一对多映射:
例一:部门-雇员关系表,Dept—Emp关系表
代码实现:
package com.company;
class Emp{
private int empno;
private String ename;
private String job;
private double sal;
private double comm;
private Emp mgr; //一个雇员有一个或没有领导
private Dept dept; //一个雇员属于一个部门
public Emp(int empno,String ename,String job,double sal,double comm){
this.empno=empno;
this.ename=ename;
this.job=job;
this.sal=sal;
this.comm=comm;
}
public void setMgr(Emp mgr){ //设置领导
this.mgr=mgr;
}
public void setDept(Dept dept){ //设置部门
this.dept=dept;
}
public Emp getMgr(){
return this.mgr;
}
public Dept getDept() {
return dept;
}
public String getInfo(){
return "雇员编号:"+empno+" 雇员姓名:"+ename+" 雇员职务:"+job+" 雇员工资:"+sal+" 雇员佣金:"+comm;
}
}
class Dept{
private int deptno;
private String dname;
private String loc;
private Emp []emps; //一个部门有多个雇员,使用对象数组
public Dept(int deptno,String dname,String loc){
this.deptno=deptno;
this.dname=dname;
this.loc=loc;
}
public void setEmps(Emp []emps){ //设置对象数组,多个雇员
this.emps=emps;
}
public Emp[] getEmps() { //特别注意返回值是一个对象数组
return emps; ///这里return 语句的格式是return +对象数组名称,而不是return +对象数组名称[]
}
public String getInfo(){
return "部门编号:"+deptno+" 部门名称:"+dname+" 部门位置:"+loc;
}
}
public class Test {
public static void main(String[] args) {
//第一步,根据结构关系设置数据
//1、设置独立对象
Emp ea = new Emp(001, "slary", "counter", 1000, 0.0);
Emp eb = new Emp(002, "mike", "Manager", 2000, 0.0);
Emp ec = new Emp(003, "frank", "CEO", 50000,0.0);
Dept dept = new Dept(102, "Accouting", "NewYork");
//2、设置关系
ea.setMgr(eb);
eb.setMgr(ec);
ea.setDept(dept);
eb.setDept(dept);
ec.setDept(dept);
dept.setEmps(new Emp[]{ea, eb, ec});
//第二步根据结构关系取出数据
System.out.println(ea.getInfo()); //输出雇员ea的信息 用来制表,避免输出格式太混乱
System.out.println("\t|-"+ea.getDept().getInfo()); //输出雇员ea的部门信息
System.out.println("\t|-"+ea.getMgr().getInfo()); //输出雇员ea的领导信息
System.out.println("**************");
System.out.println(dept.getInfo()); //输出部门信息
for (int i = 0; i < dept.getEmps().length; i++){
System.out.println(dept.getEmps()[i].getInfo()); //输出部门n内ei所有员工的信息
if(dept.getEmps()[i].getMgr()!=null){ //如果这个孤雁有领导,则继续输出该g雇员领导的信息
System.out.println("\t|-"+dept.getEmps()[i].getMgr().getInfo());
}
}
}
}
执行结果:
D:\360downloads\bin\java.exe "-javaagent:D:\360downloads\IntelliJ IDEA Community Edition 2020.3.2\lib\idea_rt.jar=56997:D:\360downloads\IntelliJ IDEA Community Edition 2020.3.2\bin" -Dfile.encoding=UTF-8 -classpath D:\java程序\out\production\java程序 com.company.Test
雇员编号:1 雇员姓名:slary 雇员职务:counter 雇员工资:1000.0 雇员佣金:0.0
|-部门编号:102 部门名称:Accouting 部门位置:NewYork //这里最前面使用了制表符来进行制表
|-雇员编号:2 雇员姓名:mike 雇员职务:Manager 雇员工资:2000.0 雇员佣金:0.0
**************
部门编号:102 部门名称:Accouting 部门位置:NewYork
雇员编号:1 雇员姓名:slary 雇员职务:counter 雇员工资:1000.0 雇员佣金:0.0
|-雇员编号:2 雇员姓名:mike 雇员职务:Manager 雇员工资:2000.0 雇员佣金:0.0
雇员编号:2 雇员姓名:mike 雇员职务:Manager 雇员工资:2000.0 雇员佣金:0.0
|-雇员编号:3 雇员姓名:frank 雇员职务:CEO 雇员工资:50000.0 雇员佣金:0.0
雇员编号:3 雇员姓名:frank 雇员职务:CEO 雇员工资:50000.0 雇员佣金:0.0
Process finished with exit code 0
例二:一对多映射,城市和省份之间的关系
代码实现:
package com.company;
class Province {
private int pid;
private String pname;
private City [] cities; //一个省份有多个城市,用对象数组
public Province(int pid,String pname){
this.pid = pid;
this.pname = pname;
}
public void setCities(City [] cities){
this.cities = cities;
}
public City[] getCities() {
return cities;
}
public String getInfo(){
return "省份编号:"+pid+" 省份名称:"+pname;
}
}
class City{
private int cid;
private String cname;
private Province pro; //一个城市属于一个省份
public City(int cid,String cname){
this.cid = cid;
this.cname = cname;
}
public void setPro(Province pro){
this.pro = pro;
}
public Province getPro() {
return pro;
}
public String getInfo(){
return "城市编号:"+cid+" 城市名城:"+cname;
}
}
public class Test {
public static void main(String[] args) {
//第一步,根据结构关系设置数据
//1、设置独立对象
City c1 = new City(001,"赣州");
City c2 = new City(002,"九江");
City c3 = new City(003,"南昌");
Province pro = new Province(112,"江西");
//2、设置关系
c1.setPro(pro);
c2.setPro(pro);
c3.setPro(pro);
pro.setCities(new City[]{c1,c2,c3}); //为省份设置城市
//第二步:根据结构关系取出数据
System.out.println(c1.getPro());
System.out.println(c1.getPro().getInfo());
System.out.println("*********************");
System.out.println(pro.getInfo());
for(int i=0;i<pro.getCities().length;i++){
System.out.println("\t|-"+pro.getCities()[i].getInfo());
}
}
}
执行结果:
D:\360downloads\bin\java.exe "-javaagent:D:\360downloads\IntelliJ IDEA Community Edition 2020.3.2\lib\idea_rt.jar=57651:D:\360downloads\IntelliJ IDEA Community Edition 2020.3.2\bin" -Dfile.encoding=UTF-8 -classpath D:\java程序\out\production\java程序 com.company.Test
com.company.Province@27d6c5e0
省份编号:112 省份名称:江西
*********************
省份编号:112 省份名称:江西
|-城市编号:1 城市名城:赣州
|-城市编号:2 城市名城:九江
|-城市编号:3 城市名城:南昌
Process finished with exit code 0
例一:商品与类型(包括子类型)之间的映射
代码实现:
package com.company;
class Item{ //父类型
private int iid;
private String name;
private String note;
private Subitem [] subitems; //一个父类型有很多子类型,使用对象数组
private Product []products; //一个父类型可以有很多的商品,使用对象数组
public Item(int iid,String name,String note){
this.iid = iid;
this.name = name;
this.note = note;
}
public void setSubitems(Subitem[] subitems) { //设置子类型对象数组
this.subitems = subitems;
}
public void setProducts(Product []products){
this.products = products;
}
public Subitem[] getSubitems() { //取得子类型对象数组
return subitems;
}
public Product[] getProducts() {
return products;
}
public String getInfo(){
return "类型编号:"+iid+" 类型名称:"+name+" 描述:"+note;
}
}
class Subitem{ //子类型
private int sid;
private String name;
private String note;
private Item item; //一个子类型属于一个父类型
private Product [] products; //一个子类型包括很多商品,使用对象数组
public Subitem(int sid,String name,String note){
this.sid = sid;
this.name = name;
this.note = note;
}
public void setItem(Item item){
this.item = item;
}
public void setProducts(Product [] products){
this.products = products;
}
public Item getItem(){
return this.item;
}
public Product[] getProducts() {
return products;
}
public String getInfo(){
return "子类型编号:"+sid+" 子类型名称:"+name+" 描述:"+note;
}
}
class Product {
private int pid;
private String name;
private double price;
private Item item; //一件商品属于一个父类型
private Subitem subitem; //一件商品属于一个子类型
public Product(int pid,String name,double price){
this.pid = pid;
this.name = name;
this.price = price;
}
public void setItem(Item item){
this.item = item;
}
public void setSubitem(Subitem subitem){
this.subitem = subitem;
}
public Item getItem() {
return item;
}
public Subitem getSubitem(){
return this.subitem;
}
public String getInfo(){
return "商品编号:"+pid+" 商品名称:"+name+" 商品价格:"+price;
}
}
public class Test {
public static void main(String[] args) {
//第一步,根据结构关系设置数据
//1、设置独立对象
Item item = new Item(001,"厨房用具","*");
Subitem suba = new Subitem(010,"刀具","*");
Subitem subb = new Subitem(011,"碗筷","*");
Subitem subc = new Subitem(012,"清理用具","*");
Product pa =new Product(110,"水果刀",5.0);
Product pb =new Product(111,"菜刀",50.0);
Product pc =new Product(112,"水晶筷",50.0);
Product pd =new Product(113,"陶瓷碗",50.0);
Product pe =new Product(114,"油烟机",5000.0);
Product pf =new Product(115,"清洁剂",50.0);
//2、设置关系
item.setSubitems(new Subitem[]{suba,subb,subc});
item.setProducts(new Product[]{pa,pb,pc,pd,pe,pf});
suba.setItem(item);
subb.setItem(item);
subc.setItem(item);
suba.setProducts(new Product[]{pa,pb});
subb.setProducts(new Product[]{pc,pd});
subc.setProducts(new Product[]{pe,pf});
pa.setItem(item);
pb.setItem(item);
pc.setItem(item);
pd.setItem(item);
pe.setItem(item);
pf.setItem(item);
pa.setSubitem(suba);
pb.setSubitem(suba);
pc.setSubitem(subb);
pd.setSubitem(subb);
pe.setSubitem(subc);
pf.setSubitem(subc);
//第二步,根据结构关系取出数据
System.out.println(item.getInfo());
for(int i=0;i<item.getSubitems().length;i++){
System.out.println("\t|-"+item.getSubitems()[i].getInfo());
}
System.out.println("-----------------------");
for(int i=0;i<item.getProducts().length;i++){
System.out.println(item.getProducts()[i].getInfo());
System.out.println("\t|-"+item.getProducts()[i].getSubitem().getInfo());
}
System.out.println("-----------------------");
System.out.println(suba.getInfo());
for(int i=0;i<suba.getProducts().length;i++){
System.out.println(suba.getProducts()[i].getInfo());
}
}
}
执行结果:
D:\360downloads\bin\java.exe "-javaagent:D:\360downloads\IntelliJ IDEA Community Edition 2020.3.2\lib\idea_rt.jar=58240:D:\360downloads\IntelliJ IDEA Community Edition 2020.3.2\bin" -Dfile.encoding=UTF-8 -classpath D:\java程序\out\production\java程序 com.company.Test
类型编号:1 类型名称:厨房用具 描述:*
|-子类型编号:8 子类型名称:刀具 描述:*
|-子类型编号:9 子类型名称:碗筷 描述:*
|-子类型编号:10 子类型名称:清理用具 描述:*
-----------------------
商品编号:110 商品名称:水果刀 商品价格:5.0
|-子类型编号:8 子类型名称:刀具 描述:*
商品编号:111 商品名称:菜刀 商品价格:50.0
|-子类型编号:8 子类型名称:刀具 描述:*
商品编号:112 商品名称:水晶筷 商品价格:50.0
|-子类型编号:9 子类型名称:碗筷 描述:*
商品编号:113 商品名称:陶瓷碗 商品价格:50.0
|-子类型编号:9 子类型名称:碗筷 描述:*
商品编号:114 商品名称:油烟机 商品价格:5000.0
|-子类型编号:10 子类型名称:清理用具 描述:*
商品编号:115 商品名称:清洁剂 商品价格:50.0
|-子类型编号:10 子类型名称:清理用具 描述:*
-----------------------
子类型编号:8 子类型名称:刀具 描述:*
商品编号:110 商品名称:水果刀 商品价格:5.0
商品编号:111 商品名称:菜刀 商品价格:50.0
Process finished with exit code 0
3.13 对象比较
判断两个基本数据类型的变量:使用 == 来进性判断
判断两个String类的属性变量:使用 equals()方法来进性比较(equalsIgnore()方法忽略大小写的差异)
判断属于同一个类的两个对象是否相同:则需要对对象里的每一个属性的内容进性比较判断是否相同(如果是自身比较,则只需要比较地址的数值是否相等即可)
范例3-88基础的比较形式
package com.company;
class Book{
private String name ;
private double price;
public void setName(String name){
this.name=name;
}
public void setPrice(double price) {
this.price = price;
}
public String getName(){
return this.name;
}
public double getPrice() {
return price;
}
public Book(String name, double price){
this.name=name;
this.price=price;
}
}
public class Main {
public static void main(String[] args) {
Book bk1 = new Book("java开发",89.0);
Book bk2 = new Book("java开发",89.0);
if(bk1.getName().equals(bk2.getName())&& bk1.getPrice()== bk2.getPrice()){
System.out.println("这两个对象是相同的");
}
else System.out.println("这两个对象不相同");
}
}
执行结果:
D:\360downloads\bin\java.exe "-javaagent:D:\360downloads\IntelliJ IDEA Community Edition 2020.3.2\lib\idea_rt.jar=58415:D:\360downloads\IntelliJ IDEA Community Edition 2020.3.2\bin" -Dfile.encoding=UTF-8 -classpath D:\java程序\out\production\java程序 com.company.Main
这两个对象是相同的
Process finished with exit code 0
分析:这个程序确实实现了两个对象之间的比较,但是却是在主方法中进行的操作,这样在实际开发中是不合理的,这样的操作应该包含在类本身中。
将比较对象的操作包含在类中,由于private封装了类中属性的这一特点,在比较时就需要·将对象作为参数传递给类中的方法·,这样在类中的方法就可以直接调用类中的属性。
注意:String 类的equals()方法也属于对象比较操作:因为String是一个类,而字符串常量则是String类的匿名对象,所以equals()方法实际上比较的是两个对象,可以发现equals()方法在对比时具备了null的验证,如果数据为null,则直接返回false
范例3-90 对象比较操作封装在类中进行
package com.company;
class Book{
private String name ;
private double price;
public Book(String name, double price){
this.name=name;
this.price=price;
}
/**
* 进行本类对象比较时,首先会判断传入的对象是否为null,然后判断地址相同,
* 如果都不相同则进行细节对比,比较属性内容是否相同,】
* 由于compare()方法接收了本类引用,所以可以直接调用私有属性
* @param temp 表示接收的对象,即要比较的对象
* @return 两个对象相同返回true,不同返回false
*/
public boolean compare(Book temp){
if(temp==null)
return false;
//执行bk1.comapare(bk2) 时会有两个对象(这两个对象之间进行比较)
//this表示当前对象(即现在调用compare()方法的对象bk1),temp表示接收的对象
if(this==temp) //传递的对象是本身时,直接比较内存地址是否相同,避免其他细节比较浪费时间
return true;
if(this.name.equals(temp.name) && this.price== temp.price){
return true;
}
else
return false;
}
}
public class Main {
public static void main(String[] args) {
Book bk1 = new Book("java开发",89.0);
Book bk2 = new Book("java开发",89.0);
if(bk1.compare(bk2)){
System.out.println("这两个对象相同");
}
else
System.out.println("这两个对象不相同");
}
}
执行结果:
D:\360downloads\bin\java.exe "-javaagent:D:\360downloads\IntelliJ IDEA Community Edition 2020.3.2\lib\idea_rt.jar=58540:D:\360downloads\IntelliJ IDEA Community Edition 2020.3.2\bin" -Dfile.encoding=UTF-8 -classpath D:\java程序\out\production\java程序 com.company.Main
这两个对象相同
Process finished with exit code 0
分析:本程序成功在类中封装了一个比较对象是否相同的compare()方法,然后在主类中调用该方法来判断两个对象是否相同,
并且由于一个类在接收了本类对象的引用时可以直接访问本类的私有属性和私有方法,所以在这个时候compare()方法就有了两个实例化对象,一个为传入的Book类对象,另一个是当前对象(即此时调用该方法的对象)
对象比较的特点:
1、本类接收自己的引用,在与本类当前对象进行比较;
2、为了避免NullPointerException的产生,应该增加一个null的判断
3、为了防止浪费性能的情况会出现,可以增加一个比较地址数值的操作,因为当与自身比较时,内存地址相同
4、进行属性内容的依次比较,如果属性内容相同,则返回true,否则返回false
3.14 static关键字
在Java中,static关键字可以用于定义属性和方法
3.14.1static关键字定义属性
一个类的主要组成就是属性和方法(分为构造方法和普通方法),而每一个对象都分别拥有各自的属性内容(不同对象的属性内容保存在不同的堆内存中)。如果类中的某一个属性希望定义公共属性(所有对象都可以使用的属性),则可以用通过static关键字来定义。
公共属性:所有对象都可以使用的属性,并且任何一个对象修改了公共属性的内容后都将影响其他对象(可以简单理解为是全局变量)
范例3-91使用static定义属性:
package com.company;
class Book{
private String name ; //普通属性
private double price;
static String pub="清华大学出版社"; //static定义的属性,暂时不封装,方便操作
public Book(String name, double price){
this.name=name;
this.price=price;
}
public String getInfo(){
return "书名:"+" 价格:"+price+" 出版社:"+pub;
}
}
public class Main {
public static void main(String[] args) {
Book bka = new Book("java开发",89.2);
Book bkb = new Book("java入门",99.2);
Book bkc = new Book("java精通",189.2);
bka.pub="北京大学出版社"; //由bka这个对象来修改pub的内容
System.out.println(bka.getInfo());
System.out.println(bkb.getInfo());
System.out.println(bkc.getInfo());
}
}
执行结果:
D:\360downloads\bin\java.exe "-javaagent:D:\360downloads\IntelliJ IDEA Community Edition 2020.3.2\lib\idea_rt.jar=59343:D:\360downloads\IntelliJ IDEA Community Edition 2020.3.2\bin" -Dfile.encoding=UTF-8 -classpath D:\java程序\out\production\java程序 com.company.Main
书名: 价格:89.2 出版社:北京大学出版社 可以看到,虽然只是由bka这个对象修改了pub的内容,但却影响力所有
书名: 价格:99.2 出版社:北京大学出版社 对象,这是因为所有对象都是同时共享这个属性的
书名: 价格:189.2 出版社:北京大学出版社
Process finished with exit code 0
可以看到,由static定义的变量并不是保存在不同的对象所特有的堆内存空间中,而是保存在全局数据区,被所有对象共享。
提示:常用内存数据区域
在Java中主要有4块内存空间:
1、栈内存空间:保存所有的对象名称(实际上是保存每个对象所拥有的堆内存空间的地址)
2、堆内存空间:保存每个对象所有的属性的内容
3、全局数据区:保存static定义的属性的内容
4、全局代码区:保存static定义的方法
static是一个公共属性的概念,那么只由一个对象去调用static定义的属性明显是不太合适的,最好的做法应该是由所有的·对象的公共代表——类来进行访问。所以static定义的属性是可以直接由类名称定义的。
例如:Book pub="北京大学出版社“;
Static属性与非static属性最大的区别:所有的非static属性必须由实例化之后的对象进行访问,而static属性不受实例化对象的控制。可以直接由类名称调用,也就是说在没有实例化对象的条件下,static属性依旧可以被访问
范例 3-92 在没有实例化对象的情况下访问static属性
package com.company;
class Book{
private String name ; //普通属性
private double price;
private static String pub="清华大学出版社"; //static定义的属性,
public Book(String name, double price){
this.name=name;
this.price=price;
}
public static void setPub(String pub) {
Book.pub = pub; //static 定义的属性是不能够用this的,因为this表示当前对象,而static定义的属性不受实例化对象的控制
}
public static String getPub() {
return pub;
}
public String getInfo(){
return "书名:"+" 价格:"+price+" 出版社:"+pub;
}
}
public class Main {
public static void main(String[] args) {
System.out.println(Book.getPub());
Book.setPub("北京大学出版社"); //修改static属性内容
Book bka = new Book("java开发",89.2);
Book bkb = new Book("java入门",99.2);
Book bkc = new Book("java精通",189.2);
System.out.println(bka.getInfo());
System.out.println(bkb.getInfo());
System.out.println(bkc.getInfo());
}
}
执行结果:
D:\360downloads\bin\java.exe "-javaagent:D:\360downloads\IntelliJ IDEA Community Edition 2020.3.2\lib\idea_rt.jar=59513:D:\360downloads\IntelliJ IDEA Community Edition 2020.3.2\bin" -Dfile.encoding=UTF-8 -classpath D:\java程序\out\production\java程序 com.company.Main
清华大学出版社 //由Book直接调用getPub()方法获取内容
书名: 价格:89.2 出版社:北京大学出版社 //由Book直接调用setPub()方法修改了static属性的内容
书名: 价格:99.2 出版社:北京大学出版社
书名: 价格:189.2 出版社:北京大学出版社
Process finished with exit code 0
3.14.2 static 定义方法
在定义类的普通·方法时也可以使用static来进行定义,使用static定义的方法也不受实例化对象的控制,可以直接由类名称调用。
package com.company;
class Book{
private String name ; //普通属性
private double price;
private static String pub="清华大学出版社"; //static定义的属性,
public Book(String name, double price){
this.name=name;
this.price=price;
}
public static void setPub(String pub) { //使用static定义方法,可以直接由类名称调用
Book.pub = pub; //static 定义的属性是不能够用this的,因为this表示当前对象,而static定义的属性不受实例化对象的控制
}
public static String getPub() {
return pub;
}
public String getInfo(){
return "书名:"+" 价格:"+price+" 出版社:"+pub;
}
}
public class Main {
public static void main(String[] args) {
System.out.println(Book.getPub());
Book.setPub("北京大学出版社"); //由类名称直接调用static定义的方法修改pub的内容
Book bka = new Book("java开发",89.2);
Book bkb = new Book("java入门",99.2);
Book bkc = new Book("java精通",189.2);
System.out.println(bka.getInfo());
System.out.println(bkb.getInfo());
System.out.println(bkc.getInfo());
}
}
执行结果:
D:\360downloads\bin\java.exe "-javaagent:D:\360downloads\IntelliJ IDEA Community Edition 2020.3.2\lib\idea_rt.jar=59513:D:\360downloads\IntelliJ IDEA Community Edition 2020.3.2\bin" -Dfile.encoding=UTF-8 -classpath D:\java程序\out\production\java程序 com.company.Main
清华大学出版社
书名: 价格:89.2 出版社:北京大学出版社
书名: 价格:99.2 出版社:北京大学出版社
书名: 价格:189.2 出版社:北京大学出版社
Process finished with exit code 0
总结:
1、static定义的属性额方法都不受实例化对象的控制,也就是说都属于独立类的功能。
2、类中的方法分为两种,static方法和非static方法,这两组方法间的访问之间有限制:
a、static方法不能直接访问非static属性和非static方法,只能调用static属性
b、非static方法可以直访问非static属性和方法,不受任何的限制
3、static定义的属性是不能用this来描述的因为this表示当前对象,而static定义的属性是不受实例化对象的控制的,所以也就不能用当前对象来描述
提示:关于static方法的访问限制的说明:
1、所有的非static定义的结构,必须在类已经明确产生实例化对象时才会分配堆内存空间,才可以使用
2、所有的Static定义的结构,不受实例化对象的控制,即可以在没有实例化对象的时候进行访问
注意:非静态方法既可以访问静态数据成员 又可以访问非静态数据成员,而静态方法只能访问静态数据成员;
非静态方法既可以访问静态方法又可以访问非静态方法,而静态方法只能访问静态数据方法。
原因:因为静态方法和静态数据成员会随着类的定义而被分配和装载入内存中,而非静态方法和非静态数据成员只有在类的对象创建时在对象的内存中才有这个方法的代码段。
来自 https://blog.csdn.net/qq_28511781/article/details/71405945
提示:关于主方法的操作问题
1·、如果在主类中定义主方法,并且由主方法直接调用,定义时必须要使用static,语法格式:
Public static 返回值类型 方法名称(参数,参数,参数…){
编写操作代码;
[return语句]
}
2、编写类时,类中的方法定义时不需要使用static,(由对象调用)语法格式如下:
Public 返回值类型 方法名称(参数,参数,参数…){
编写操作代码;
[return语句]
}
3、对这两者的区别的解释:
主方法实际上是主类中的方法,并且使用了static来进行定义,而static定义的方法只能访问static定义的方法,所以当一个方法在主类中定义并由主方法直接调用的话就必须使用static定义,否则主方法将不能调用。
如果主类中定义方法时没使用static定义,就必须由主类的对象来调用,即 主类对象.方法()或者 new 主类名称 ().方法提示:在定义类(不是主类)时使用static
定义方法时一般有两种情况:
1、需要使用类名称来调用的方法
2、不保存普通属性时定义static方法
产生实例化对象是因为在堆内存空间章可以保存属性信息,所以如果一个类中没有属性产生,自然就没有必要开辟堆内存,这个时候就可以考虑使用static来定义方法
范例3-94主类中 static定义方法
public class Main {
public static void main(String[] args) {
fun();
}
public static void fun(){
System.out.println("hello world");
}
}
范例3-95 主方法中调用非static定义的方法
public class Main {
public static void main(String[] args) {
new Main().fun(); //使用主类的匿名对象来调用非static定义的方法
//这里还可以先实例化一个主类的对象来调用fun()方法
//Main method = new Main();
//method.fun();
}
public void fun(){
System.out.println("hello world");
}
}
3.14.3主方法
Java最大的特点就在于主方法,因为Java中的主方法的组成单元很多。
Java中主方法的组成单元:
1、public:主方法时程序的开始,所以这个方法对任何操作都是可见的,既然是可见的那就应该是
public的
2、static:证明该方法由类名称直接调用
3、void:主方法是一切执行的起始点,没有返回值
4、main:是一个系统规定好的方法名称,不能修改
5、String args[]:指的是一个程序运行时传递·的参数,格式为:类名称 参数 参数 参数 (如果参数本身具有空格,则需要使用”“”"来进行声明
3.14.4 static 的实际应用
static关键字的特点:
1、不管由多少个对象,都使用同一个static属性
2、使用static方法可以避免实例化对象的限制
static的实际应用:
功能一:统计实例化对象的个数
因为实例化对象时一定会调用构造方法,所以可以在构造方法中利用static属性来记录实例化对象的个数,
代码实现:
package com.company;
class Book{
private String name ; //普通属性
private double price;
private static String pub="清华大学出版社"; //static定义的属性,
private static int num=0;
public Book(){
num++;
System.out.println("这是第"+num+"个对象");
}
public Book(String name, double price){
num++;
System.out.println("这是第"+num+"个对象");
this.name=name;
this.price=price;
}
}
public class Main {
public static void main(String[] args) {
new Book();
new Book();
new Book();
new Book();
new Book();
}
}
执行结果:
D:\360downloads\bin\java.exe "-javaagent:D:\360downloads\IntelliJ IDEA Community Edition 2020.3.2\lib\idea_rt.jar=59929:D:\360downloads\IntelliJ IDEA Community Edition 2020.3.2\bin" -Dfile.encoding=UTF-8 -classpath D:\java程序\out\production\java程序 com.company.Main
这是第1个对象
这是第2个对象
这是第3个对象
这是第4个对象
这是第5个对象
Process finished with exit code 0
提示:本程序只能实现记录个数增加,
在本程序中构造方法主要是在创建新对象时调用的,所以可以通过构造方法来实现对象个数的统计,但是当某一个对象不再使用时,应该进行实例化对象的减少,这一操作将在第11章finalize()方法实现。
功能二:实现属性的自动设置
可以设置一个static变量,在实例化对象调用了无参构造时可以在无参构造中利用static属性来为某一个属性来设置内容。
3.15代码块
在程序编写中可以直接使用 { } 定义一段语句,根据此部分定义的位置可以以及声明的关键字可以分为4种代码块:
普通代码块、构造块、静态块、同步代码块(等待多线程时)
3.15.1普通代码块
如果一个代码块写在方法里,就称为普通代码块
范例3-98:编写普通代码块
package com.company;
public class Main {
public static void main(String[] args) {
{//普通代码块 (该代码块定义在方法中(主方法)中,所以是普通代码块)
int num=10; //定义局部变量
System.out.println("num="+num);
}
int num=100;
System.out.println("num="+num);
}
}
执行结果:
D:\360downloads\bin\java.exe "-javaagent:D:\360downloads\IntelliJ IDEA Community Edition 2020.3.2\lib\idea_rt.jar=61751:D:\360downloads\IntelliJ IDEA Community Edition 2020.3.2\bin" -Dfile.encoding=UTF-8 -classpath D:\java程序\out\production\java程序 com.company.Main
num=10
num=100
Process finished with exit code 0
分析:本程序在普通代码块里定义了一个num变量,在普通代码块的外部定义了一个num变量,由于第一个num变量定义在普通代码块中,所以是个局部变量,第二个num变量定义在普通代码块外部,相对来说就是全局变量,这两个变量不会互相影响,一般而言普通代码块可以实现较大的程序分隔,这样可以很好的避免变量重名的问题。
提示:全局变量和·局部变量是一种相对性的概念
全局变量和局部变量是针对定义代码的情况而定的,只是一种相对性的概念。
3.15.2构造块
如果将一个代码块写在类中,这个代码块就是构造块。
范例3-100 定义构造块
package com.company;
class Book{
public Book(){ //构造方法
System.out.println("A:Book类的构造方法");
}
{ //该代码块定义在类里,所以是个构造快
System.out.println("B:Book类的构造块");
}
}
public class Main {
public static void main(String[] args) {
new Book();
new Book();
}
}
执行·结果:
D:\360downloads\bin\java.exe "-javaagent:D:\360downloads\IntelliJ IDEA Community Edition 2020.3.2\lib\idea_rt.jar=61826:D:\360downloads\IntelliJ IDEA Community Edition 2020.3.2\bin" -Dfile.encoding=UTF-8 -classpath D:\java程序\out\production\java程序 com.company.Main
B:Book类的构造块
A:Book类的构造方法
B:Book类的构造块
A:Book类的构造方法
Process finished with exit code 0
分析:构造块在每一次实例化对象时都会被调用,而且调用顺序优先于构造方法执行。
3.15.3静态块
如果一个代码块使用static定义,就称其为静态块,静态块分为两种情况:在非主类中使用、在主类中定义。
情况一:在非主类中使用:
package com.company;
class Book{
public Book(){ //构造方法
System.out.println("A:Book类的构造方法");
}
{ //该代码块定义在类里,所以是个构造快
System.out.println("B:Book类的构造块");
}
static{ //定义静态块
System.out.println("C:Book类的静态块");
}
}
public class Main {
public static void main(String[] args) {
new Book();
new Book();
}
}
执行结果:
D:\360downloads\bin\java.exe "-javaagent:D:\360downloads\IntelliJ IDEA Community Edition 2020.3.2\lib\idea_rt.jar=61864:D:\360downloads\IntelliJ IDEA Community Edition 2020.3.2\bin" -Dfile.encoding=UTF-8 -classpath D:\java程序\out\production\java程序 com.company.Main
C:Book类的静态块
B:Book类的构造块
A:Book类的构造方法
B:Book类的构造块
A:Book类的构造方法
Process finished with exit code 0
分析:当有多个·实例化对象产生时。静态块会优先调用,并且只调用一次,
小总结:实例化对象时的调用顺序:静态块>构造块>构造方法
静态块的主要作用就是为static初始化属性:
范例 3-101:利用静态块为static属性初始化
package com.company;
class Book{
static String msg; //static属性,暂时不封装
public Book(){ //构造方法
System.out.println("A:Book类的构造方法");
}
{ //该代码块定义在类里,所以是个构造快
System.out.println("B:Book类的构造块");
}
static{ //定义静态块
msg="hello".substring(0,2); //为static属性设置内容
System.out.println("C:Book类的静态块");
}
}
public class Main {
public static void main(String[] args) {
new Book();
new Book();
System.out.println(Book.msg); //static属性可以直接由类名称调用
}
}
执行结果:
D:\360downloads\bin\java.exe "-javaagent:D:\360downloads\IntelliJ IDEA Community Edition 2020.3.2\lib\idea_rt.jar=61902:D:\360downloads\IntelliJ IDEA Community Edition 2020.3.2\bin" -Dfile.encoding=UTF-8 -classpath D:\java程序\out\production\java程序 com.company.Main
C:Book类的静态块
B:Book类的构造块
A:Book类的构造方法
B:Book类的构造块
A:Book类的构造方法
he
Process finished with exit code 0