Java面向对象
来源:互联网 发布:jquery.format.js 编辑:程序博客网 时间:2024/05/17 21:39
3.1、面向对象(了解)
面向对象是一种现在最为流行的程序设计方法,几乎现在的所有应用都以面向对象为主了,最早的面向对象的概念实际上是由IBM提出的,在70年代的Smaltalk语言之中进行了应用,后来根据面向对象的设计思路,才形成C++,而由C++产生了Java这门面向对象的编程语言。
但是在面向对象设计之前,广泛采用的是面向过程,面向过程只是针对于自己来解决问题。面向过程的操作是以程序的基本功能实现为主,实现之后就完成了,也不考虑修改的可能性,面向对象,更多的是要进行子模块化的设计,每一个模块都需要单独存在,并且可以被重复利用,所以,面向对象的开发更像是一个具备标准的开发模式。
在面向对象定义之中,也规定了其一些基本的特征:
· 封装:保护内部的操作不被破坏;
· 继承:在原本的基础之上继续进行扩充;
· 多态:在一个指定的范围之内进行概念的转换。
对于面向对象的开发来讲也分为三个过程:OOA(面向对象分析)、OOD(面向对象设计)、OOP(面向对象编程)。
3.2、类与对象(核心)
3.2.1、类与对象的基本概念
类:是抽象的概念集合,表示的是一个共性的产物,类之中定义的是属性和行为(方法);
对象:对象是一种个性的表示,表示一个独立的个体,每个对象拥有自己独立的属性,依靠属性来区分不同对象。
那么可以依靠一句话来总结出类和对象的区别:类是对象的模板,对象是类的实例,类只有通过对象才可以使用,而在开发之中应该先产生类,之后再产生对象。
类不能直接使用,对象是可以直接使用的。
3.2.2、类与对象的定义
如果要在Java之中定义类的话,可以使用class关键字完成,其语法如下:
class 类名称 {
属性 (变量) ;
行为 (方法) ;
}
范例:定义一个Person类
class Person { // 类名称首字母大写
String name ;
int age ;
public void tell() { // 没有static
System.out.println("姓名:" + name + ",年龄:" + age) ;
}
}
那么类定义完成之后,肯定无法直接使用,如果要使用,必须依靠对象,那么由于类属于引用数据类型,所以对象的产生格式如下。
格式一:声明并实例化对象
类名称对象名称 = new类名称 () ;
格式二:分步完成
声明对象:
类名称对象名称 = null ;
实例化对象:
对象名称 = new 类名称 () ;
以后只要是引用数据类型的实例化操作,永远都会存在关键字new(分配空间)。当一个实例化对象产生之后,可以按照如下的方式进行类的操作:
·对象.属性:表示调用类之中的属性;
·对象.方法():表示调用类之中的方法。
范例:使用对象操作类
class Person { // 类名称首字母大写
String name ;
int age ;
public void tell() { // 没有static
System.out.println("姓名:" + name + ",年龄:" + age) ;
}
}
public class TestDemo {
public static void main(String args[]) {
Person per = new Person() ; // 声明并实例化对象
per.name = "张三" ;
per.age = 30 ;
per.tell() ;
}
}
以上完成了一个基本的类和对象的操作关系,下面换另外一个操作来观察一下。
class Person { // 类名称首字母大写
String name ;
int age ;
public void tell() { // 没有static
System.out.println("姓名:" + name + ",年龄:" + age) ;
}
}
public class TestDemo {
public static void main(String args[]) {
Person per = null ; // 声明对象
per = new Person() ; // 实例化对象
per.name = "张三" ;
per.age = 30 ;
per.tell() ;
}
}
疑问?给出的两种不同的实例化方式有什么区别呢?
如果要想解释这个问题,那么首先需要解决的就是内存的关系理解(Java是在C++之上开发的,所以本次讲解的时候还是按照C++的理论进行内存关系的讲解),首先给出两块内存空间:
·堆内存:保存对象的真正数据,都是每一个对象的属性内容;
·栈内存:保存的是一块堆内存的空间地址,可以把它想象成一个int型变量(每一个int型变量只能存放一个数值),所以每一块保留一块堆内存地址,但是为了方便理解,可以简单的将栈内存之中保存的数据理解为对象的名称(Person per),就假设保存的是per。
按照这样的概念理解,以上的程序就可以变成如下的内存关系图表示出来。如果要想开辟堆内存空间,只能依靠关键字new来进行开辟。即:只要看见了关键字new不管何种情况下,都表示要开辟新的堆内存空间。
范例:观察产生两个对象的操作
class Person { // 类名称首字母大写
String name ;
int age ;
public void tell() { // 没有static
System.out.println("姓名:" + name + ",年龄:" + age) ;
}
}
public class TestDemo {
public static void main(String args[]) {
Person per1 = null ; // 声明对象
Person per2 = new Person() ; // 声明并实例化对象
per1 = new Person() ; // 实例化对象
per1.name = "张三" ;
per1.age = 30 ;
per2.name = "李四" ;
per2.age = 20 ;
per1.tell() ;
per2.tell() ;
}
}
但是在这里需要提醒的是,如果在开发之中出现了以下代码:
class Person { // 类名称首字母大写
String name ;
int age ;
public void tell() { // 没有static
System.out.println("姓名:" + name + ",年龄:" + age) ;
}
}
public class TestDemo {
public static void main(String args[]) {
Person per = null ; //声明对象
per.name = "张三" ;
per.age = 30 ;
per.tell() ;
}
}
这个时候的程序发现只声明了Person对象,但是并没有实例化Person对象(只有了栈内存,并没有对应的堆内存空间),则程序在编译的时候不会出现任何的错误,但是在执行的时候出现了以下的错误信息:
Exception in thread "main" java.lang.NullPointerException
at TestDemo.main(TestDemo.java:11)
这个错误信息表示的是“NullPointerException(空指向异常)”,这种异常只会在引用数据类型上产生,此异常会一直伴随着大家,到你不写程序的那一天。
3.2.3、引用传递的初步深入
下面通过若干个程序,以及程序的内存分配图,来进行代码的讲解。
范例:观察以下程序的结果
class Person { // 类名称首字母大写
String name ;
int age ;
public void tell() { // 没有static
System.out.println("姓名:" + name + ",年龄:" + age) ;
}
}
public class TestDemo {
public static void main(String args[]) {
Person per1 = new Person() ; // 声明并实例化对象
per1.name = "张三" ;
per1.age = 20 ;
Person per2 = per1 ; // 引用传递
per2.name = "李四" ;
per1.tell() ;
}
}
引用传递的精髓:同一块堆内存空间,同时被多个栈内存所指向,不同的栈可以修改同一块堆内存的内容。
范例:观察以下程序的运行
class Person { // 类名称首字母大写
String name ;
int age ;
public void tell() { // 没有static
System.out.println("姓名:" + name + ",年龄:" + age) ;
}
}
public class TestDemo {
public static void main(String args[]) {
Person per1 = new Person() ; // 声明并实例化对象
Person per2 = new Person() ;
per1.name = "张三" ;
per1.age = 20 ;
per2.name = "李四" ;
per2.age = 30 ;
per2 = per1 ;
per2.name = "王五" ;
per1.tell() ;
}
}
垃圾:指的是在程序开发之中没有任何对象所指向的一块堆内存空间,这块空间就成为垃圾,所有的垃圾将等待GC(垃圾收集器)不定期的进行回收与空间的释放。
范例:观察如下的程序(恶心点的)
class Person { // 类名称首字母大写
String name ;
int age ;
public void tell() { // 没有static
System.out.println("姓名:" + name + ",年龄:" + age) ;
}
}
public class TestDemo {
public static void main(String args[]) {
Person per1 = new Person() ; // 声明并实例化对象
Person per2 = new Person() ;
per1.name = "张三" ;
per1.age = 20 ;
per2.name = "李四" ;
per2.age = 30 ;
Person per3 = per1 ;
per3.name = "王五" ;
per3 = per2 ;
per1 = per2 ;
per2.name = per3.name ;
per3.age = per1.age ;
per2.tell() ;
}
}
首先本程序是几乎是不可能在开发之中出现的,如果真的出现也是不可能的。
下面还是通过内存关系分析,但是强调几个代码:
per2.name = per3.name ;
per3.age = per1.age ;
· “per2.name = per3.name ;”:将per3的name属性的给per2.name属性;
· “per3.age = per1.age ;”:将per1的age属性给per3.age属性。
3.3、封装性(重点)
封装属于面向对象的第一大特性,但是本次所讲解的封装只是针对于其中的一点进行讲解,而对于封装操作由于涉及的内容过多,以后会有完整的介绍。但是在讲解封装操作之前,首先先要来解决一个问题:为什么要有封装?
范例:观察没有封装操作的情况
class Person { // 类名称首字母大写
String name ;
int age ;
public void tell() { // 没有static
System.out.println("姓名:" + name + ",年龄:" + age) ;
}
}
public class TestDemo {
public static void main(String args[]) {
Person per = new Person() ;
per.name = "张三" ;
per.age = -30 ;
per.tell() ;
}
}
发现这个时候所设置的人的年龄是“-30”岁,结果从代码编译上不会有问题,但是从实际来讲,一个人的年龄不可能是-30岁,这个是属于业务逻辑出错。而造成这种错误的关键在于没有检查,用户直接操作。就好比银行,你自己能直接操作金库?而检查的第一步是需要让用户看不见操作的东西,那么在这种情况下,就可以使用private关键字,将类之中的属性进行私有化的操作。
class Person { // 类名称首字母大写
private String name ;
private int age ;
public void tell() { // 没有static
System.out.println("姓名:" + name + ",年龄:" + age) ;
}
}
public class TestDemo {
public static void main(String args[]) {
Person per = new Person() ;
per.name = "张三" ; // TestDemo.java:11:错误: name可以在Person中访问private
per.age = -30 ; // TestDemo.java:12:错误: age可以在Person中访问private
per.tell() ;
}
}
所以现在属性是安全了,而如果现在外部要想操作私有属性,那么按照Java的开发标准而言,现在需要按照如下形式定义操作方法:setter、getter:
· setter(private Stringname):publicvoid setName(String n);
· getter(private String name):public String getName();
class Person { // 类名称首字母大写
private String name ;
private int age ;
public void setName(String n) {
name = n ;
}
public void setAge(int a) {
age = a ;
}
public String getName() {
return name ;
}
public int getAge() {
return age ;
}
public void tell() { // 没有static
System.out.println("姓名:" + name + ",年龄:" + age) ;
}
}
public class TestDemo {
public static void main(String args[]) {
Person per = new Person() ;
per.setName("张三") ;
per.setAge(20) ;
per.tell() ;
}
}
以上的代码只是可以访问了,不过没有验证,但是问题是验证在那块加?应该在setter之中增加检查操作。
class Person { // 类名称首字母大写
private String name ;
private int age ;
public void setName(String n) {
name = n ;
}
public void setAge(int a) {
if (a >= 0 && a <= 250) {
age = a ;
}
}
public String getName() {
return name ;
}
public int getAge() {
return age ;
}
public void tell() { // 没有static
System.out.println("姓名:" + name + ",年龄:" + age) ;
}
}
public class TestDemo {
public static void main(String args[]) {
Person per = new Person() ;
per.setName("张三") ;
per.setAge(20) ;
per.tell() ;
}
}
以后在进行开发的时候,如果有需要,则在setter上加入一些验证措施,而getter方法只是简单的将数据返回即可,不需要做任何的验证。
疑问?为什么现在没有在程序之中使用getter()方法?
现在类之中的getName()和getAge()两个方法虽然被定义了,但是发现并没有被使用,那么这样的定义还有意义吗?
在类之中的属性定义setter、getter操作方法目的就是为了设置和取得属性的内容,也许某一个操作暂时不使用到取得的操作,不过从开发来讲,必须全部提供。以后在定义类的时候,所有的属性都要编写private封装,封装之后的属性如果需要被外部操作,则编写setter、getter。
3.4、构造方法(重点)
在之前强调过方法和属性的区分,方法之后存在“()”,而属性之后什么都没有,如果要想清楚构造方法,则首先来观察以下的格式:
类名称 对象名称 = new 类名称() ;
这种操作格式在之前已经使用过了,那么下面可以针对于这个格式每一个出现的标记进行解释:
· 类名称(类名称 对象名称 = new 类名称() ;):要定义变量的数据类型;
· 对象名称(类名称 对象名称 = new 类名称() ;):指的是日后进行类属性或方法操作的名称;
· new(类名称 对象名称 = new 类名称() ;):开辟堆内存空间;
· 类名称()(类名称 对象名称 = new 类名称();):???
按照道理来讲,加上“()”都属于方法,但是这个方法稍微特殊一些,属于构造方法,所以这个时候就可以发现,构造方法和普通方法不太一样的地方;构造方法是在实例化对象的时候使用,而普通方法是在实例化对象产生之后使用的。
构造方法本身的定义如下:
·构造方法的名称和类名称保持一致;
·构造方法不允许有返回值类型声明;
·由于对象实例化操作一定需要构造方法的存在,所以如果在类之中没有明确定义构造方法的话,则会自动的生成一个无参的,无返回值的构造方法,供用户使用,如果一个类之中已经明确的定义了一个构造方法的话,则无参的什么都不做的构造方法将不会自动生成,也就是说,一个类之中至少存在一个构造方法。
class Person { // 类名称首字母大写
public Person() {// 无参无返回值的方法
}
}
·构造方法在对象实例化的时候完成操作,而且一个对象的构造方法只会显式调用一次。
当然,在类之中也可以明确的定义一个构造方法,可以通过构造方法为类之中的属性初始化。
class Person { // 类名称首字母大写
private String name ;
private int age ;
public Person(String n,int a) { //通过构造方法赋值
name = n ;
setAge(a) ; // 调用本类中的setAge()方法,可以检查
}
public void setName(String n) {
name = n ;
}
public void setAge(int a) {
if (a >= 0 && a <= 250) {
age = a ;
}
}
public String getName() {
return name ;
}
public int getAge() {
return age ;
}
public void tell() { // 没有static
System.out.println("姓名:" + name + ",年龄:" + age) ;
}
}
public class TestDemo {
public static void main(String args[]) {
Person per = new Person("张三",-20) ;
per.tell() ;
}
}
当然,如果现在非要强调所调用的方法是本类之中所定义方法的时候,也可以在方法前增加一个this,例如:
public Person(String n,int a) { // 通过构造方法赋值
this.setName(n) ;
this.setAge(a) ; // 调用本类中的setAge()方法,可以检查
}
这里的this就表示本类之中的方法调用,加与不加结果完全相同。
在实际之中构造方法的主要作用只有一点:在对象实例化的时候为类之中的属性初始化。
当然,对于构造方法而言,也需要注意,那么可以进行重载,不过构造方法重载的时候只需要考虑参数的类型及个数即可,而方法名称肯定是相同的。
范例:观察构造方法重载
class Person { // 类名称首字母大写
private String name ;
private int age ;
public Person(){}
public Person(String name) {
this.setName(name) ;
}
public Person(String n,int a) { //通过构造方法赋值
this.setName(n) ;
this.setAge(a) ; // 调用本类中的setAge()方法,可以检查
}
public void setName(String n) {
name = n ;
}
public void setAge(int a) {
if (a >= 0 && a <= 250) {
age = a ;
}
}
public String getName() {
return name ;
}
public int getAge() {
return age ;
}
public void tell() { // 没有static
System.out.println("姓名:" + name + ",年龄:" + age) ;
}
}
public class TestDemo {
public static void main(String args[]) {
Person per = new Person("张三") ;
per.tell() ;
}
}
可是对于构造方法重载强调一点,编写顺序:所有的重载的方法按照参数的个数由多到少,或者是由少到多排列。
额外提醒(暂时无法验证):构造方法实际上严格来讲是属于整个对象构造过程的最后一步,对象的构造需要为其分配空间,之后为其设置默认值,最后留给构造方法进行其他操作,所以可以定义的构造方法是对象实例化的最后一步,而其他的几步用户根本就看不见,也无法操作,那么对于如下的代码:
class Person { // 类名称首字母大写
private String name = "张三" ;
public Person() { // 是构造的最后一步
System.out.println(name) ;
}
}
public class TestDemo {
public static void main(String args[]) {
Person per = new Person() ;
}
}
这个类之中的name属性只有在构造完成之后,才可以为其赋予“张三”的内容,如果构造没有执行,则name就是其对应数据类型的默认值:null。
3.5、匿名对象(重点)
没名字的对象称为匿名对象,对象的名字按照之前的内存关系来讲,在栈内存之中,而对象的具体内容在堆内存之中保存,这样一来,没有栈内存指向堆内存空间,就是一个匿名对象。
class Person { // 类名称首字母大写
private String name ;
private int age ;
public Person(String n,int a) {
name = n ;
age = a ;
}
public void tell() {
System.out.println("姓名:" + name + ",年龄:" + age) ;
}
}
public class TestDemo {
public static void main(String args[]) {
new Person("张三",20).tell() ; // 匿名对象
}
}
如果不习惯使用匿名对象,以后的对象就都起名字。匿名对象由于没有对应的栈内存指向,所以只能使用一次,一次之后就将成为垃圾,并且等待被GC回收释放。
3.6、思考题(核心)
现在要求定义一个表示雇员的操作类,这个类之中包含雇员编号、姓名、职位、基本工资、 佣金,并且可以计算出一个雇员的月薪和年薪,可以返回一个雇员的完整信息(把所有的属性内容都返回)。
参考:之前讲解的Person程序。
简单Java类:一个类之中只包含基本的属性、setter、getter方法,这种类称为简单java类,对于简单Java类的开发原则有如下几点,必须严格遵守:
·类名称必须可以明确的表示出一类的定义,例如:Person、Emp、Dept;
·类之中的所有属性必须使用private进行封装;
·类之中的所有属性都必须定义相应的setter、getter;
·类之中可以提供构造方法,为属性初始化,但是不管提供了多少个构造方法,一定要保留有一个无参构造;
·类之中不允许直接使用System.out.println()输出,所有的内容要返回给被调用处输出。
在一个类之中编写的时候,应该按照如下顺序:属性、构造、普通方法。
class Emp { // 雇员类
private int empno ;
private String ename ;
private String job ;
private double sal ;
private double comm ;
public Emp(){}
public Emp(int eno,String ena,String j,double s,double c){
empno = eno ;
ename = ena ;
job = j ;
sal = s ;
comm = c ;
}
public void setEmpno(int eno) {
empno = eno ;
}
public void setEname(String ena) {
ename = ena ;
}
public void setJob(String j) {
job = j ;
}
public void setSal(double s) {
sal = s ;
}
public void setComm(double c) {
comm = c ;
}
public int getEmpno() {
return empno ;
}
public String getEname() {
return ename ;
}
public String getJob() {
return job ;
}
public double getSal() {
return sal ;
}
public double getComm() {
return comm ;
}
public double salary() {
return sal + comm ;
}
public double income() {
return this.salary() * 12 ;
}
public String getInfo() {
return "雇员信息:" + "\n" +
"\t|- 编号:" + this.getEmpno() + "\n" +
"\t|- 姓名:" + this.getEname() + "\n" +
"\t|- 职位:" + this.getJob() + "\n" +
"\t|- 工资:" + this.getSal() + "\n" +
"\t|- 佣金:" + this.getComm() + "\n" +
"\t|- 月薪:" + this.salary() + "\n" +
"\t|- 年薪:" + this.income() ;
}
}
public class TestDemo {
public static void main(String args[]) {
Emp emp = new Emp(7369,"SMITH","CLERK",800.0,0.0) ;
System.out.println(emp.getInfo()) ;
}
}
作业:写出10个简单Java类,自己随便去定义,类之中的属性不能少于5个,多多的。
}public int getEmpno() {
return empno ;
}
public String getEname() {
return ename ;
}
public String getJob() {
return job ;
}
public double getSal() {
return sal ;
}
public double getComm() {
return comm ;
}
public double salary() {
return sal + comm ;
}
public double income() {
return this.salary() * 12 ;
}
public String getInfo() {
return "雇员信息:" + "\n" +
"\t|- 编号:" + this.getEmpno() + "\n" +
"\t|- 姓名:" + this.getEname() + "\n" +
"\t|- 职位:" + this.getJob() + "\n" +
"\t|- 工资:" + this.getSal() + "\n" +
"\t|- 佣金:" + this.getComm() + "\n" +
"\t|- 月薪:" + this.salary() + "\n" +
"\t|- 年薪:" + this.income() ;
}
}
}
3.7、数组(重点)
数组属于引用型数据,所以数组的操作过程之中,也一定会牵扯到内存的分配问题。
3.7.1、数组的基本概念
例如,现在要求定义100个整型变量,那么按照之前所学的概念来讲,要定义成:int i1,i2 … i100,表示100个整型变量,这样的确是可以定义,但是这些变量不方便管理,可以通过数组管理,数组:就是一组相关变量的集合。
数组的定义格式如下(动态操作格式):
格式一:声明并开辟(实例化)数组
数据类型数组名称 [] = new数据类型 [长度] ;
数据类型 [] 数组名称 = new数据类型 [长度] ;
格式二:分步完成
声明数组:
数据类型数组名称 [] = null ;
开辟数组:
数组名称 = new数据类型 [长度] ;
当数组开辟完成空间之后,可以利用“数组名称[索引]”的方式来操作数组,而索引的范围为:0 ~ 数组长度-1,例如:开辟了三个大小的数组,则索引的取值是:0、1、2,一共三个内容。
在默认情况下,以上的格式为数组的动态初始化,动态初始化的最大特点,在于开辟空间之后,里面的所有数据的内容都是其对应数据类型的默认值,如果是int,默认值是0。
由于数组是通过一个名称统一的进行管理,所以数组在输出的时候往往可以利用for循环完成,for循环需要知道明确的循环次数,那么数组数据的输出循环次数就是数组长度,而数组长度的取得“数组名称.length”。
范例:定义并使用一个数组
public class TestDemo {
public static void main(String args[]) {
int data [] = new int [3] ; // 开辟3个空间的数组
data [0] = 10 ;
data [1] = 20 ;
data [2] = 30 ;
for (int x = 0 ; x < data.length ; x ++) {
System.out.print(data[x] + "、") ;
}
}
}
以上程序的内存分析图操作如下。
整个的操作流程和对象操作几乎是一样的,无外乎在对象之中保存的是属性,而在数组之中保存的是各个索引元素。
范例:数组的引用传递
public class TestDemo {
public static void main(String args[]) {
int data [] = new int [3] ; // 开辟3个空间的数组
data [0] = 10 ;
data [1] = 20 ;
data [2] = 30 ;
int temp [] = data ; // 类型统一,起了别名
temp [0] = 100 ;
for (int x = 0 ; x < data.length ; x ++) {
System.out.print(data[x] + "、") ;
}
}
}
以上的是采用了声明并开辟空间的数组初始化方式完成的,下面采用分步的方式完成。
public class TestDemo {
public static void main(String args[]) {
int data [] = null ; // 声明数组
data = new int [3] ; // 开辟3个空间的数组
data [0] = 10 ;
data [1] = 20 ;
data [2] = 30 ;
for (int x = 0 ; x < data.length ; x ++) {
System.out.print(data[x] + "、") ;
}
}
}
如果这个时候在使用数组的过程之中,没有进行实例化操作?
public class TestDemo {
public static void main(String args[]) {
int data [] = null ; //声明数组
data [0] = 10 ;
data [1] = 20 ;
data [2] = 30 ;
for (int x = 0 ; x < data.length ; x ++) {
System.out.print(data[x] + "、") ;
}
}
}
Exception in thread "main" java.lang.NullPointerException
at TestDemo.main(TestDemo.java:4)
如果在操作的过程之中,超出了数组本身的访问界限。
public class TestDemo {
public static void main(String args[]) {
int data [] = new int [3] ; // 开辟3个空间的数组
data [5] = 10 ;
}
}
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 5
at TestDemo.main(TestDemo.java:5)
从现在的开发来讲,可以很负责任的说,数组一定会出现,但是真心的讲不多。因为数组最麻烦的问题在于它的长度限制上。
3.7.2、数组的静态初始化
数组的动态初始化在开辟空间之后,里面的所有内容都是其对应数据类型的默认值,那么也可以使用静态初始化的方式,为数组在开辟空间时,设置好指定的内容,而数组的静态初始化的语法格式如下:
格式一:简写格式
数据类型数组名称 [] = {值,值,...} ;
数据类型 [] 数组名称= {值,值,...} ;
格式二:完整格式(推荐使用)
数据类型数组名称 [] = new数据类型 [] {值,值,...} ;
数据类型 [] 数组名称 = new数据类型 [] {值,值,...} ;
范例:数组的静态初始化
public class TestDemo {
public static void main(String args[]) {
int data [] = new int [] {209,201,2,2,3,6,7} ;
for (int x = 0 ; x < data.length ; x ++) {
System.out.println(data[x]) ;
}
}
}
到底在开发之中使用何种数组初始化的方式并没有一个明确的定义,还是看功能,如果你已经知道了所有的操作数据,那么使用静态合适,如果有一些数据需要单独配置,那么用动态合适。
3.7.3、数组与方法(难点)
既然数组内容可以进行引用传递,那么就可以把数组给方法之中的参数,如果一个方法要想接收参数,则对应的参数类型必须是数组,下面来观察如下的一道程序。
范例:使用方法接收数组
public class TestDemo {
public static void main(String args[]) {
int data [] = new int [] {209,201,2,2,3,6,7} ;
print(data) ;// 引用传递,int temp [] = data ;
}
public static void print(int temp []) {
for (int x = 0 ; x < temp.length ; x ++) {
System.out.print(temp[x] + "、") ;
}
System.out.println() ;
}
}
范例:判断某一个数据是否在指定的数组之中
基本操作原理:使用数组的静态初始化,声明一个数组内容,而后随便设置一个数据,判断这个数据是否在指定的数组之中存在,如果存在则提示存在,不存在,则提示不存在。
public class TestDemo {
public static void main(String args[]) {
int data [] = new int [] {209,201,2,2,3,6,7} ;
int searchData = 3 ; // 要查找的内容
boolean flag = false ; // 查找标记
// flag = true:表示查找到内容
// flag = false:表示没有查找到内容
for (int x = 0 ; x < data.length ; x ++) {
if (searchData == data[x]) { // 找到了
flag = true ; // 找到
break ; // 退出循环
}
}
if (flag) { // 为true
System.out.println("已经查找到了内容。") ;
} else {
System.out.println("没有找到内容。") ;
}
}
}
可是,在以上的代码之中,发现主方法里面的程序太多了,我们的目标是:让主方法的代码变的越少越好,所以应该定义一个查找方法,那么这个查找方法应该返回boolean型数据。这个方法要接收两个参数:要查找的数据、数组。
public class TestDemo {
public static void main(String args[]) {
int data [] = new int [] {209,201,2,2,3,6,7} ;
int searchData = 3 ; // 要查找的内容
if (isExists(data,searchData)) { // 为true
System.out.println("已经查找到了内容。") ;
} else {
System.out.println("没有找到内容。") ;
}
}
public static boolean isExists(int temp [],int search) { // 存在
for (int x = 0 ; x < temp.length ; x ++) {
if (temp[x] == search) {
return true ; // 查找到了,后面的循环不做了
}
}
return false ; // 没有查找到
}
}
以上的两个程序,只是完成了方法接收数组的操作,但是并没有在方法之中对数组的内容进行修改,而由于是引用传递,方法也是可以对数组进行修改的。下面首先通过一个简单的代码来观察。
public class TestDemo {
public static void main(String args[]) {
int data [] = new int [] {1,2,3,4,5} ; // 5个元素
inc(data) ;
print(data) ;
}
// 数组之中的每一个元素扩大两倍
public static void inc(int temp []) { // 接收数组
for (int x = 0 ; x < temp.length ; x ++) {
temp[x] *= 2 ;
}
}
public static void print(int temp []) {
for (int x = 0 ; x < temp.length ; x ++) {
System.out.print(temp[x] + "、") ;
}
System.out.println() ;
}
}
范例:数组排序的操作
数组排序是在笔试之中经常会见到的一个最基本的程序。下面就随便讲解一个冒泡程序的实现。
下面按照这个思路,进行代码实现。
public class TestDemo {
public static void main(String args[]) {
int data [] = new int [] {1,3,2,6,10,0,5,8} ;
for (int x = 0 ; x < data.length ; x ++) {
for (int y = 0 ; y < data.length - 1 ; y ++) {
if (data[y] > data[y + 1]) { // 后面的小
int t = data[y] ; // 第三方接收
data[y] = data[y + 1] ;
data[y + 1] = t ;
}
}
}
print(data) ;
}
public static void print(int temp []) {
for (int x = 0 ; x < temp.length ; x ++) {
System.out.print(temp[x] + "、") ;
}
System.out.println() ;
}
}
但是现在主方法之中的代码过多了,可以编写一个排序方法。
public class TestDemo {
public static void main(String args[]) {
int data [] = new int [] {1,3,2,6,10,0,5,8} ;
sort(data) ;
print(data) ;
}
public static void sort(int temp []) {
for (int x = 0 ; x < temp.length ; x ++) {
for (int y = 0 ; y < temp.length - 1 ; y ++) {
if (temp[y] > temp[y + 1]) { // 后面的小
int t = temp[y] ; // 第三方接收
temp[y] = temp[y + 1] ;
temp[y + 1] = t ;
}
}
}
}
public static void print(int temp []) {
for (int x = 0 ; x < temp.length ; x ++) {
System.out.print(temp[x] + "、") ;
}
System.out.println() ;
}
}
以上的所有操作都是指方法接收数组进行操作,而方法也可以返回数组,那么这个时候只需要将返回值类型定义为数组即可。
public class TestDemo {
public static void main(String args[]) {
int data [] = init() ; // 返回数组接收
print(data) ;
}
public static int [] init() {
int temp [] = new int [] {1,3,2,6,10,0,5,8} ;
return temp ;
}
public static void print(int temp []) {
for (int x = 0 ; x < temp.length ; x ++) {
System.out.print(temp[x] + "、") ;
}
System.out.println() ;
}
}
思考题:要求定义一个方法,这个方法可以统计出数组的最大值、最小值、总和、平均值(忽略小数)
下面首先分着进行操作。
·统计出数组最大值:假设第一个数据是最大值,如果后面有比这个数据还要大的,修改最大值
public class TestDemo {
public static void main(String args[]) {
int data [] = new int [] {1,3,2,6,10,0,5,8} ;
int max = data[0] ; // 假设第一个为最大值
for (int x = 0 ; x < data.length ; x ++) {
if (max < data[x]) { // 后面的数据大
max = data[x] ; // 修改max的内容
}
}
System.out.println(max) ;
}
}
· 最小值:
public class TestDemo {
public static void main(String args[]) {
int data [] = new int [] {1,3,2,6,10,0,5,8} ;
int min = data[0] ; // 假设第一个为最大值
for (int x = 0 ; x < data.length ; x ++) {
if (min > data[x]) { // 后面的数据大
min = data[x] ; // 修改max的内容
}
}
System.out.println(min) ;
}
}
· 总和:所有数据累加:
public class TestDemo {
public static void main(String args[]) {
int data [] = new int [] {1,3,2,6,10,0,5,8} ;
int sum = 0 ; // 保存结果
for (int x = 0 ; x < data.length ; x ++) {
sum += data[x] ; // 累加
}
System.out.println(sum) ;
}
}
· 平均值:
public class TestDemo {
public static void main(String args[]) {
int data [] = new int [] {1,3,2,6,10,0,5,8} ;
int sum = 0 ; // 保存结果
for (int x = 0 ; x < data.length ; x ++) {
sum += data[x] ; // 累加
}
System.out.println(sum / data.length) ;
}
}
· 综合操作,以上的程序是分步完成了数据的计算,可是现在要求的是通过一个方法,取得统计结果。既然是需要方法的统计,那么这个统计的结果就需要返回给调用处,可是一个方法只能返回一种数据类型,现在需要多种数据?可以将返回值定义为数组,而数组之中一共包含四个元素:数组[0] = 最大值、数组[1] = 最小值、数组[2] = 总和、数组[3] = 平均值。
public class TestDemo {
public static void main(String args[]) {
int data [] = new int [] {1,3,2,6,10,0,5,8} ;
int st [] = stat(data) ;
System.out.println("最大值:" + st[0]) ;
System.out.println("最小值:" + st[1]) ;
System.out.println("总 和:" + st[2]) ;
System.out.println("平均值:" + st[3]) ;
}
public static int [] stat(int temp[]) { // 统计
int result [] = new int [4] ; // 长度为4个数组
result[0] = temp[0] ; // 假设第一个元素为最大值
result[1] = temp[0] ; // 假设第一个元素为最小值
for (int x = 0 ; x < temp.length ; x ++) {
if (result[0] < temp[x]) { // 需要更改最大值
result[0] = temp[x] ;
}
if (result[1] > temp[x]) { // 需要更改最小值
result[1] = temp[x] ;
}
result[2] += temp[x] ; // 总和
}
result[3] = result[2] / temp.length ; // 平均值
return result ;
}
}
以后可能这种复杂的操作不会出现的很多,但是数组是作为逻辑训练的一个有效手段。
3.7.4、与数组有关的操作方法
如果要想确定一门语言的优秀,那么一定要看这门语言对开发者的支持度有多好,对于数组的操作,在Java之中提供了两种操作方法:
1、 数组排序:java.util.Arrays.sort(数组名称)
public class TestDemo {
public static void main(String args[]) {
int data [] = new int [] {1,3,2,6,10,0,5,8} ;
java.util.Arrays.sort(data) ; //排序
print(data) ;
}
public static void print(int temp []) {
for (int x = 0 ; x < temp.length ; x ++) {
System.out.print(temp[x] + "、") ;
}
System.out.println() ;
}
}
这种操作是在工作之中使用的(工作之中也真没见过使)。
面试题:请编写一个数组排序操作
public class TestDemo {
public static void main(String args[]) {
int data [] = new int [] {1,3,2,6,10,0,5,8} ;
sort(data) ;
print(data) ;
}
public static void sort(int temp []) {
for (int x = 0 ; x < temp.length ; x ++) {
for (int y = 0 ; y < temp.length - 1 ; y ++) {
if (temp[y] > temp[y + 1]) { //后面的小
int t = temp[y] ; //第三方接收
temp[y] = temp[y + 1] ;
temp[y + 1] = t ;
}
}
}
}
public static void print(int temp []) {
for (int x = 0 ; x < temp.length ; x ++) {
System.out.print(temp[x] + "、") ;
}
System.out.println() ;
}
}
使用java.util.Arrays.sort()也可以排序。
2、 数组拷贝,从一个数组之中拷贝部分内容到另外一个数组之中
·方法:System.arraycopy(源数组名称,源数组开始点,目标数组名称,目标数组开始点,拷贝长度) ;
例如,现在给定两个数组:
·数组A:1、2、3、4、5、6、7、8、9;
·数组B:11、22、33、44、55、66、77、88、99。
将数组B的部分内容替换到数组A中,数组A的最终结果:1、2、66、77、88、6、7、8、9。
public class TestDemo {
public static void main(String args[]) {
int dataA [] = new int [] {1,2,3,4,5,6,7,8,9} ;
int dataB [] = new int [] {11,22,33,44,55,66,77,88,99} ;
System.arraycopy(dataB,5,dataA,2,3) ;
print(dataA) ;
}
public static void print(int temp []) {
for (int x = 0 ; x < temp.length ; x ++) {
System.out.print(temp[x] + "、") ;
}
System.out.println() ;
}
}
这些操作都属于固定的功能,写熟练即可。
3.7.5、二维数组
之前所定义的数组只有一个“[]”,就表示一维数组,如果有两个“[]”就表示二维数组,但是下面通过一个简单的表格,来区分出一维和二维数组的区别。
对于二维数组,本身也存在着两种实例化格式:
格式一:动态初始化
数据类型 数组名称 [][] = new 数组名称 [行数] [列数] ;
格式二:静态初始化
数据类型 数组名称 [][] = new 数组名称 [] [] {
{值,值,...},{值,值,...},{值,值,...},...} ;
范例:观察二维数组
public class TestDemo {
public static void main(String args[]) {
int data [][] = new int [][] {
{1,2,3},{54,6},{8,9,10,16}} ;
for (int x = 0 ; x < data.length ; x ++) {
for (int y = 0 ; y < data[x].length ; y ++) {
System.out.print(data[x][y] + "、") ;
}
System.out.println() ;
}
}}
3.7.6、对象数组
之前所讲解的全部数组,都属于基本数据类型的数组,但是如果现在要想表示出多个对象,那么就需要对象数组的概念,而对象数组的定义格式和之前是完全一样,只是把数据类型换成类即可。
格式一:对象数组的动态初始化
类名称 对象数组名称 = new 类名称 [长度] ;
如果使用了对象数组的动态初始化,则默认情况下,里面的每一个元素都是其对应的默认值null,都需要分别进行对象的实例化操作。
格式二:对象数组的静态初始化
类名称 对象数组名称 = new 类名称 [] {实例化对象,实例化对象,...} ;
范例:对象数组的动态初始化
class Person {
private String name ;
private int age ;
public Person(String n,int a) {
name = n ;
age = a ;
}
public String getInfo() {
return "姓名:" + name + ",年龄:" + age ;
}
}
public class TestDemo {
public static void main(String args[]) {
Person per [] = new Person[3] ; // 对象数组
per[0] = new Person("张三",20) ;
per[1] = new Person("李四",21) ;
per[2] = new Person("王五",22) ;
for (int x = 0 ; x < per.length ; x ++) {
System.out.println(per[x].getInfo()) ;
}
}
}
范例:对象数组的静态初始化
class Person {
private String name ;
private int age ;
public Person(String n,int a) {
name = n ;
age = a ;
}
public String getInfo() {
return "姓名:" + name + ",年龄:" + age ;
}
}
public class TestDemo {
public static void main(String args[]) {
Person per [] = new Person[] {
new Person("张三",20),new Person("李四",21),
new Person("王五",22)} ; // 对象数组
for (int x = 0 ; x < per.length ; x ++) {
System.out.println(per[x].getInfo()) ;
}
}
}
从概念上讲,对象数组的唯一优势,就是可以包含多个对象进行操作。
4、总结
1、 类和对象的定义、内存分配、引用传递;
2、 构造方法的定义结构,基本概念;
3、 使用private实现的封装性和setter、getter方法定义要求;
4、 简单java类的定义原则;
5、 数组的基本概念,熟悉即可。
5、预习任务
String类的特点及常用方法、this关键字。
6、作业
1、 将一个给定的一维整型数组转置输出,
例如: 源数组,1 2 3 45 6
转置之后的数组,6 5 4 3 2 1
按照同理,完成一个二维数组的行列转换并输出。
2、 现在有如下的一个数组:
intoldArr[]={1,3,4,5,0,0,6,6,0,5,4,7,6,7,0,5} ;
要求将以上数组中值为0的项去掉,将不为0的值存入一个新的数组,生成的新数组为:
intnewArr[]={1,3,4,5,6,6,5,4,7,6,7,5} ;
3、 现在给出两个数组:
• 数组A:“1,7,9,11,13,15,17,19:;
• 数组b:“2,4,6,8,10”
两个数组合并为数组c,按升序排列。
- java----Java面向对象
- java----Java面向对象
- java----Java面向对象
- java----Java面向对象
- java----Java面向对象
- java----Java面向对象
- 【java】java面向对象
- 【Java】Java面向对象
- java面向对象(一) 了解面向对象
- Java面向对象概述
- java面向对象
- JAVA面向对象
- JAVA 面向对象??
- Java面向对象特性
- Java面向对象特性
- java基础 面向对象
- Java面向对象程序设计
- java面向对象
- rdtsc指令,测量程序的运行速度
- 从单一WAR到多活, 记述一个创业公司的架构演变
- 搭建和编写第一个maven程序
- 百度地图JAVA与Matlab平台经纬度距离角度位置迭代方法
- FZU 1669 Right-angled Triangle(解毕达哥拉斯三元组)
- Java面向对象
- Python turtle 学习之画个五角星
- iOS中的delegate的用法和规范
- Java奇淫巧技之Lombok
- java实现properties文件读写
- 清除浮动的几种方法
- Spring Boot 部署与服务配置
- Sentinel-1A/B参考手册
- JAVA中isEmpty和null以及""的区别