第四章 面向对象编程

版权声明:转载麻烦告知。 https://blog.csdn.net/qq_40064948/article/details/81741076

一、面向对象初识

以对象为导向的程序设计,简称oop(object orientated programming)。

1.1面向对象与面向过程

区别:

面向过程:事物比较简单,可以用线性的思维去解决。

面向对象:事物比较复杂,使用简单的线性思维无法解决。

共同点:

面向过程和面向对象都是解决实际问题的一种思维方式。二者相辅相成,并不是对立的。解决复杂问题,通过面向对象方式便于我们从宏观上把握事物之间复杂的关系、方便我们分析整个系统;具体到微观操作,任然使用面向过程方式来处理。

例如:

如何开汽车(事物比较简单,可以用线性的思维去解决)-面向过程:踩离合、挂挡、踩油门、放离合、开了。-面向对象:驾驶员、汽车、驾驶员开汽车、car.start();。

如何造汽车(事物比较复杂,使用简单的线性思维无法解决)-面向过程:造车轮?造发动机?造车皮?挡风玻璃?……很难决定上面这些步骤之间的关系!先造发动机还是车轮。-面向对象:车轮、发动机、车壳、桌椅、挡风玻璃……将上面的造出来的东西,组装,汽车造出。

1.2面向对象需要掌握的知识点

二、面向对象细节

2.1类-对象

public class Person {
    public static void main(String[] args) {
        Student stu1 = new Student();
        Student stu2 = new Student( "张三" , 15 , '男' );
        stu1.study();
        stu2.text();
    }
}
class Student{
    String name ;
    int age ;
    char sex ;
    //无参构造和带参数的构造
    public Student(){
    }
    public Student(String name , int age , char sex){
        this.name = name ;
        this.age = age ;
        this.sex = sex;
    }
    public void study(){
        System.out.println("学生要学习");
    }
    public static void text(){
        System.out.println("学生要考试");
    }
}

2.1.1类和对象的概念

是对对象的抽象(抽象 抽出象的部分)(Person与Student都是类,Student在这里是一个类的引用。)类是封装对象的属性和行为的载体,反过来说具有相同属性和行为的一类实体被称为类。例如一个鸟类,鸟类封装了所有鸟的共同属性和应具有的行为。

对象是具体的事物 (下面代码中:stu指向的张三 new Sudent()这个整体 就是对象)

现实世界中,随处可见的一种事物就是对象,对象是事物存在的实体,如人类、书桌、计算机、高楼大厦等。人类解决问题的方式总是将复杂的事物简单化,于是就会思考这些对象都是由哪些部分组成的。通常都会将对象划分为两个部分,即动态部分与静态部分。静态部分,顾名思义就是不能动的部分,这个部分被称为“属性”,任何对象都会具备其自身属性,如一个人,它包括高矮、胖瘦、性别、年龄等属性。然而具有这些属性的人会执行哪些动作也是一个值得探讨的部分,这个人可以哭泣、微笑、说话、行走,这些是这个人具备的行为(动态部分),人类通过探讨对象的属性和观察对象的行为了解对象。

2.1.2知识点

:class。

对象:Object,instance(实例)。以后我们说某个类的对象,某个类的实例,是一个意思。一个完整的对象,可能有属性,一定有方法(行为)。

对象和类的关系:

  • 特殊到一般,具体到抽象。
  • 类可以看成一类对象的模板,对象可以看成该类的一个具体实例。
  • 类是用于描述同一类型的对象的一个抽象的概念,类中定义了这一类对象所应具有的静态和动态属性。

JDK提供了很多供编程人员使用的类,编程人员也可定义自己的类。

定义类:属性、方法、构造方法、其他(代码块、静态代码块、内部类)

创建对象:类名 对象名= new 类名(); Person p = new Person();使用已经定义好的类,创建该类对象的过程称为“实例化”。

调用类的属性和方法:对象名.成员变量对象名.成员方法。(.)这个是成员运算符,来访问类的属性和方法。

2.2类-属性

2.2.1认识属性

属性field,或者叫成员变量。属性用于定义该类或该类对象包含的数据或者说静态属性。属性作用范围是整个类体

在定义成员变量时可以对其初始化,如果成员变量不赋初值,那么引用型为null,原始新为0。boolean-false\char-null\byte-0\short-0\int-0\long-0L\float-0.0f\double-0.0d\.S

属性定义格式:

2.2.2局部变量和成员变量

区别:

  • 声明位置不同,成员变量类中、局部变量方法中。
  • 作用范围不同:成员变量当前类的方法、局部变量当前方法。
  • 内存存放的位置不同:成员变量栈内存中、局部变量堆内存中。
  • 成员变量有默认值、局部变量没有默认值。

2.2.3 方法的调用

形参和实参:定义方法的参数是形式参数,调用方法的参数是实在参数,调用方法时要求参数个数相同,类型兼容。

参数传递:基本数据类型的参数传递(无法通过方法调用改变变量的值),引用数据类型的参数传递(可以通过方法调用改变变量的值)

2.3类-引用类型

Java语言中除基本类型之外的变量类型都称之为引用类型。Class继承了object,class也是object类的类。

2.3.1 与指针的区别

Java中的对象和数组是通过引用对其操作的。

  • 引用可以理解为一种受限的指针。
  • 指针是可以进行与证书做加减运算的,而两个指针之间也可以进行大小比较运算和相减运算。引用不行,只能进行赋值运算。
  • 引用就是一个变量或对象的别名(引用的本质是一个对象);指针是一个段内存空间的地址(指向存储一个变量值的空间或一个对象的空间)

2.3.2内存分析

栈:存放:局部变量。先进后出,自下而上存储。方法执行完毕,自动释放空间

堆:存放new出来的对象 只要有new就是在堆区开辟空间。需要垃圾回收器来回收

方法区:存放:类的信息(代码)、static(变量)、字符串常量等。

2.4类-构造器

public class Person {
	public static void main(String[] args) {
		Student stu1 = new Student();
		Student stu2 = new Student( "张三" , 15 , '男' );
		stu1.study();
		stu2.text();
	}
}
class Student{
	String name ;
	int age ;
	char sex ;
	public Student(){
	}
	public Student(String name , int age , char sex){
		this.name = name ;
		this.age = age ;
		this.sex = sex;
	}
}

构造器定义:constructor构造方法。一个在创建对象时被自动调用的特殊方法。

构造器作用:为对象进行初始化(成员变量)工作。

构造器是一种特殊的方法:

  • 构造器的方法名必须和类名一致。
  • 通过new关键字调用。new关键词都是在堆开空间。
  • 如果我们没有定义构造器,则系统会自动定义一个无参的构造方法。如果已定义则编译器不会添加无参数构造方法。
  • 与普通方法一样,构造方法也可以重载。

三、封装

隐藏对象内部的复杂性,只对外公开简单的借口。便于外界调用,从而提高系统的可扩展性、可维护性。

3.1使用访问控制符实现封装

成员访问权限有四种:

  • public 公共的(可以被项目中所有的类访问——项目可见性)
  • protected 受保护的(可以被这个类本身访问,同一个包中的所有其他的类访问,被它的子类(同一个包以及不同包中的子类)访问)
  • default/friendly 默认的/友好的 (被这个类本身访问,被同一个包中的类访问——包可见性)这个权限,在没有定义类的访问权限时,系统会自动认为这个类为friendly或者default,default是JDK1.5之后出来的新关键词
  • private 私有的 (只能被这个类本身访问——类可见性)

类的访问权限只有两种:

  • public 公共的(可被同一项目中所有的类访问,必须与文件名同名)
  • default/friendly 默认的/友好的 (可被同一个包中的类访问)

封装的要点:

类的属性的处理:一般使用private(除非本属性确定会让子类继承),提供相应的get/set方法来访问相关属性,这些方法通常是public,从而提供对属性的读取操作(注意:boolean变量的get方法是用is开头)。一些只用于本类的辅助性方法可以用private,希望其他类调用的方法用public。

3.2 this

class Test {
	private String name ;
	public Test(){
	}
	public Test(String name){
		//调用无参构造
		this();
		//区分成员变量和局部变量,在这里后面的name属于局部变量也就是通过方法形参传进来的实参
		this.name = name ;
	}
	public void Run(){
		//调用自身的方法
		this.RunQuiky();
	}
	public void RunQuiky(){
		System.out.println("跑得快……");
	}
}

this表示的是当前对象本身,更准确地说,this代表当前对象的一个引用。

普通方法中使用this:区分类成员属性和方法的形参,调用当前对象的其他方法。位置:任意。

构造方法中使用this:使用this来调用其他构造方法,位置:必须是第一条语句。

this不能用于static方法。(this属于对象,static属于类,当类被加载时,static的代码就被加载,但是这个时候还没有对象,所以this在这里不能用。)

3.3 static(共享的 静态的)

public class Test {
	int a;
	static int width;
	public static void main(String[] args){
		Test t = new Test();
		Test.width = 2;
		Test.gg();
		//通过引用也可以访问static变量或static方法。不过一般还是使用类名.static成员名来访问。
		t.gg();
		gg();
	}
	public static void gg(){
		System.out.println("gg");
	}
	public void tt(){
		System.out.println("tt");
	}	
}

3.3.1静态成员变量与静态方法

static声明的成员变量为静态成员变量,或者叫做:类属性,类变量。在类被载入时被显示初始化。被该类的所有对象共享。

static声明的方法为静态方法,不需要对象,就可以调用(类名.方法名),在调用该方法时,不会将对象的引用传递给它。静态方法不能以任何方式引用this和super关键字。

3.3.2静态变量和方法的调用

静态属性(变量和方法)隶属于类,类加载,静态内容就会加载到内存中(方法区),在内存中只有一份内存拷贝。

静态内容调用非静态内容:需要创建对象,因为非静态内容隶属于对象,先有类再有对象,非静态内容调用静态内容:可以使用“对象.类属性”来调用。不过,一般都是用“类名.类属性”。

3.3.3静态初始化块

static int i ;
static{
	i = 3;
	System.out.println("i");
}

如果希望加载后,对整个类进行某些初始化操作,可以使用static初始化块。类第一次被载入时先执行static代码块,类多次载入时,static代码块只执行一次,static经常用来进行static变量的初始化。是在类初始化时执行,不是在创建对象时执行。静态初始化块中不能访问非static成员。

3.4 案列:理解static static{} {} 构造的执行顺序

public class Test {
	public static void main(String[] args) {
		new B();
		new A();
		A.sub();
	}
}
class A{
	static B b = new B();
	static{
		System.out.println("a_1");
	}
	{
		System.out.println("a_2");
	}
	public void add() {
		System.out.println("a_add");
	}
	public static void sub(){
		System.out.println("a_sub");
	}
}
class B{
	static{
		System.out.println("b_1");
	}
	{
		System.out.println("b_2");
	}
	public B(){
		System.out.println("b_B");
	}
	public void add() {
		System.out.println("b_add");
	}
	public static void sub(){
		System.out.println("b_sub");
	}
}

输出结果为:b_1  b_2  b_B  b_2  b_B  a_1  a_2  a_sub

static修饰的属性与方法,类加载时被放在缓存区,等调用者调用的时候才会执行代码。static{}修饰的属性与方法在类被加载时就会执行一次,每加载一次就会执行一次,上图的代码A、B两个类都只加载了一次,所以static{}里面的代码只执行了一次。{}是代码块,每调用一次B类,就会执行一次代码块的内容。构造里的代码只会在new一个对象的时候才会调用。

3.5包

3.5.1 package

一般情况下载当前类首行(通常是类的第一句非注释性语句)通过package关键词声明当前类的所在位置。每个类在创建时都需要制定当前类的包名。是一个类的唯一标识:包名.类名。package解决了类之间的重名问题,防止调用同名的类。为了便于管理类:合适的类位于合适的包。包名:域名倒着写即可,再加上模块名,并与内部管理类。

注意事项——写项目时都要加包,不要使用默认包。com.gao和com.gao.car,这两个包没有包含关系,是两个完全独立的包。只是逻辑上看起来后者是前者的一部分。

3.5.2 import导包

//导入该包下的Scanner类
import java.util.Scanner;
//导入该包下的所有类
import java.util.*;

作用——如果不使用import,我们如果用到其他包的类时,只能这么写:java.util.Date,代码量太大,不利于编写和维护。通过import可以导入其他包下面的类,从而可以在本类中直接通过类名来调用。

如果导入两个同名的类,要么一个导包一个写全限定名,要么两个都写全限定名。

java会默认导入java.lang包下所有的类,因此这些类我妈可以直接使用。

//导入Math类的所有静态属性
import static java.lang.Math.*;
//导入Math类的PI属性
import static  java.lang.Math.PI;

JDK1.5以后加入了新特性——import static,这样我们可以直接在程序中使用静态属性。

四、继承

public class GameTest {
	public static void main(String[] args) {
		System.out.println("--------------怪物-");
		Gost g = new Gost();
		g.move();
		g.fexFun();
		System.out.println("--------------蛇妖-");
		Snake s1 = new Snake("蛇妖",5,"20");
		s1.showS();
		System.out.println("--------------蜈蚣-");
		Wugong w1 = new Wugong("蜈蚣",100,"33");
		w1.showW();
	}
}
class Gost{
	private String name;
	private int life;
	private String fex;
	public Gost(){
	}
	public Gost(String name , int life , String fex){
		this.name = name ;
		this.life = life ;
		this.fex = fex ;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public int getLife() {
		return life;
	}
	public void setLife(int life) {
		this.life = life;
	}
	public String getFex() {
		return fex;
	}
	public void setFex(String fex) {
		this.fex = fex;
	}
	//攻击方法
	public void fexFun(){
		System.out.println("super.fexFun()");
	}
	//移动方法
	public void move(){
		System.out.println("super.move()");
	}
}
class Snake extends Gost{
	public Snake(){
	}
	public Snake(String name ,int life, String fex){
		super(name,life,fex);
	}
	//蛇的补血技能
	public void returnLife(int life){
		if(life < 10){
			this.setLife(this.getLife() + 20);
			System.out.println("实施大蛇补血术……小蛇蛇的life+20,当前生命值:"+this.getLife());
		}
	}
	@Override
	public void move() {
		System.out.println("我扭扭扭");
	}
	@Override
	public void fexFun(){
		System.out.println("怪物蛇蛇展开攻击");
	}
	public void showS(){
		System.out.println(this.getName()+"展开攻击");
		System.out.println("当前生命值是:"+this.getLife());
		System.out.println("攻击力是:"+this.getFex());
		this.returnLife(this.getLife());
		this.move();;
	}
}

class Wugong extends Gost{
	public Wugong(){	
	}
	public Wugong(String name ,int life, String fex){
		super(name,life,fex);
	}
	@Override
	public void fexFun(){
		System.out.println("怪物蜈蚣展开攻击");
	}
	@Override
	public void move() {
		System.out.println("我飞飞飞");
	} 
	public void showW(){
		System.out.println(this.getName()+"展开攻击");
		System.out.println("当前生命值是:"+this.getLife());
		System.out.println("攻击力是:"+this.getFex());
		this.move();;
	}
}

4.1认识继承

继承的语法:class student extends Person{}

类是对对象的抽象,继承是对某一批类的抽象,从而实现对现实世界更好的建模。有不同的叫法:超类、根类、父类、派生类。构造不能被继承,构造方法是先构造父类再构造子类。

子类继承父类的成员变量和成员方法,但不继承父类的构造方法。java中只有单继承没有像c++那样的多继承,多继承会引起混乱,是的继承链过于复杂,系统难于维护。就像我们现实中,如果你有多个父母亲,就混乱了。多继承,就是为了实现代码的复用性,却引入了复杂性,是的系统类之间的关系混乱。java中的多继承,可以通过接口来实现。

如果定义一个类时,没有调用extends,则它的父类是java.lang.Object;

4.2方法的重写

public class TestOverride { 
	public static void main(String[] args) {
		Animal animal = new Animal();
		animal.shout();
		Dog dog = new Dog();
		dog.shout();
	}
}
class Animal{
	public void shout(){
		System.out.println("发出声音!");
	}
}
class Dog extends Animal {
	public void shout(){
		System.out.println("旺旺旺!");
	}
}

在子类中可以根据需要对从父类中继承来的方法进行重写。方法重写必须和被重写方法具有相同方法名称参数列表返回类型。重写方法不能使用被重写方法更严格的访问权限(多态)。

4.3Object类

public static void main(String[] args) {
	Object obj = new Object();
	//用于比较引用类型是否相等
	obj.equals(obj); 
 	////反射 获取当前对象对应Class对象。返回的是当前对象所在类的完全限定名
 	obj.getClass();   
	//返回一个int类型的 hash值
 	obj.hashCode();   
	//输出当前对象的hash值
 	obj.toString();   		
 	 //线程
 	obj.notify();
 	//线程
 	obj.wait();  
}

查阅API了解Object中的6个常用方法的声明方法及作用。

Object是所有类的父类。

4.4super

public class Test { 
	public static void main(String[] args) {
		new ChildClass().f();
	}
}
class FatherClass {
    public int value;
    public void f(){
        value = 100;
        System.out.println
        ("FatherClass.value="+value);
    }
}
class ChildClass extends FatherClass {
    public int value;
    public void f() {
        super.f();   
        value = 200;
        System.out.println
             ("ChildClass.value="+value);
        System.out.println(value);
        System.out.println(super.value);
    }
}

super是直接父类对象的引用。可以通过super来访问父类中被子类覆盖的方法或属性。

super可以显示的调用父类的构造方法。在使用super调父类构造的时候,必须写在子类构造方法中的第一行

普通方法中:没有顺序限制,可以随便调用。

在构造函数中:任何类的构造函数中,若是构造函数的第一行代码没有显示的调用super(),那么java默认都会调用super(),作为父类的初始化函数。所以你这得super()加不加都无所谓。

构造方法调用顺序:根据super的说明,构造方法第一句,总是super()来调用父类对应的构造方法。先向上追溯到Objcet,然后再一次向下执行类的初始化块和构造方法,知道当前子类为止。

4.5对象的比较 == 和 equals()

==:比较两基本类型变量的值是否相等。比较两个引用类型的值即内存地址是否相等,即是否指向同一对象。

equals():两对象的内容是否一致。

自定义类必须重写equals(),否则其对象比较结果总是false。

String类型的数值在比较时,如果数值在byte的范围内即-128到127,那么系统会认为这是一个int型数据,当超过这个数值时,系统变认为是String。

五、多态polymorphism

5.1认识多态

妹子带男朋友回家之前,丈母娘问男朋友长得怎样,妹子说有点像港台明星(港台明星有刘德华、曾志伟),女朋友把男朋友带回家,丈母娘一看,疯了!原来是“曾志伟”。

多态是oop中一个重要特性,主要是用来实现动态联编的,换句话说,就是程序的最终状态只有在执行过程中才被决定而非在编译期间就决定了。这对于大型系统来说能提高系统的灵活性和扩展性。

java中如何实现多态?使用多态的好处?——引用变量的两种类型:编译时类型(由声明时的类型决定),运行时类型(由实际对应的对象类型决定,比如形参是Object obj,实际在运行时传的实参为Object的子类,String 自定义类 等,此时也是多态。)

多态存在要有3个必要条件:要有继承,要有方法重写,父类引用指向子类对象

5.2引用数据类型的类型转换

子类转换为父类:自动转换。

上转型对象不能操作子类新增的成员变量和方法。

上转型对象可以操作子类继承或重写的成员变量和方法。

如果子类重写了父类的某个方法,上转型对象调用该方法时,是调用的重写方法。

父类转换为子类:强制转换。

绝不是做手术,而是父类的真面目就是一个子类,否则会出现类型转换错误。

5.3final

final可以用来修饰变量,方法,类。修饰变量:变量一旦被初始化便不可改变,相当定义了一常量。修饰方法:final方法是在子类中不能被覆盖的方法。修饰类:final类是无法被任何类继承的。

5.4abstract抽象

public class Test{
	public static void main(String[] args) {
		Plant p1 = new Tree();
		p1.grow();
	}
}
//如果一个类继承了抽象类 但是没有重写父类中所有的抽象方法 当前类需要定义为抽象类
abstract class Herb extends Plant{
	public void grow(){
		System.out.println("Herb.grow()");
	}
	//这里的Herb草本植物类没有光合作用的功能,不用重写photosynthesis();但是不写会报错,所以需要将这个Herb类定义为抽象类,那么不重写这个父类抽象方法就不会报错。
	//@Override
	//public void photosynthesis() {
	//	System.out.println("一呼一吸之间解决战斗");
	//}
}
abstract class Plant{
	private String color;
	//不能自己new 这个构造是给子类准备的 因为子类的构造中有默认的super构造
	public Plant() {
	}
	public Plant(String color) {
		super();
		this.color = color;
	}
	public String getColor() {
		return color;
	}
	public void setColor(String color) {
		this.color = color;
	}
	//生长
	public abstract void grow();
	//光合作用
	public abstract void photosynthesis();
}
class Tree extends Plant{
	private int age;
	public Tree() {
	}
	public int getAge() {
		return age;
	}
	public void setAge(int age) {
		this.age = age;
	}
	//生长
	@Override
	public void grow(){
		System.out.println("一年涨一圈");
	}
	@Override
	public void photosynthesis() {
		System.out.println("一呼一吸之间解决战斗");
	}
}
class Flower extends Plant{
	private int num;
	public Flower() {
	}
	public int getNum() {
		return num;
	}
	public void setNum(int num) {
		this.num = num;
	}
	// 生长
	@Override
	public void grow(){
		System.out.println("三个月涨一圈");
	}
	@Override
	public void photosynthesis() {
		System.out.println("一呼一吸之间解决战斗");
	}
}

抽象类是一种模板模式,抽象类为所有子类提供了一个通用模板,子类可以在这个模板基础上进行扩展。通过抽象类,可以避免子类设计的随意性。通过抽象类,我们就可以做到严格限制子类的设计,使子类之间更加通用。

通过abstract修饰的类,称之为抽象类,抽象类中可以存在抽象方法。

抽象类不能实例化,但是有构造器,给子类准备的。子类继承的父类如果是抽象方法,子类必须要重写父类中所有的抽象方法。抽象类中定义的方法可以是抽象方法,也可以是普通方法。

abstract抽象类就是用来继承的,抽象方法就是用来被重写的。

final修饰的类用来防止继承,修饰的方法就是用来防止重写的。

5.5interface 接口

接口就是比“抽象类”还“抽象”的“抽象类”,可以更加规范的对子类进行约束。全面地专业地实现了:规范和具体实现的分离。

接口就是规范定义的是一组规则,体现了现实世界中“如果你是……则必须能……”的思想。接口的本质是契约,就像我们人间的法律一样,制定好后大家都遵守。项目的具体需求是多变得,我们必须以不变应万变才能从容开发,此处的“不变”就是“规范”。因此,我们开发项目往往都是面向接口编程

如何定义接口:

interface Test extends A,B{
	public static final double PI = 3.14;
	public abstract void add();
}

接口相关规则:接口中所有方法都是抽象的。即使没有显示的将接口中的成员用public标识,也是public访问类型的。接口中变量默认用public static final标识,所以接口中定义的变量就是全局静态常量

可以定义一个新接口,用extends去继承一个(或多个,用“,”隔开)已有的接口。可以定义一个,用implements去实现一个接口中所有方法。可以定义一个抽象类,用implements去实现一个接口中部分方法。

5.6内部类

将一个类定义置入另一个类定义中就叫做“内部类”,内部类分类:成员内部类、静态内部类、方法内部类、匿名内部类

内部类都存在一个外部类对象的引用:外部类名.this

5.6.1成员内部类

其他类里创建内部类的方式:

  • 先导包,在创建外部类对象.创建内部类对象
import com.cn.Outer01.Inner01;

Inner01 inner = new Outer01().new Inner01();
  • 不需要导包:
Outer01.Inner01 inner = new Outer01().new Inner01();

内部类可以直接使用外部类的属性、方法,外部类使用内部类的属性、方法则需要在外部类中创建内部类对象。

内部类作为外部类成员,可声明为private(使用private修饰类之后,就不能再用导包的方式去创建内部类对象)、默认、protected或public。内部类成员只有在内部类的范围之内是有效的,所以缩小生命周期。用内部类定义在外部类中不可访问的属性,这样就在外部类中实现了比外部类的private还要小的访问权限。

编译后生成两个类:Outer.class和Outer$Inner.class。

public class A {
	private String s = "1";
	public void  aa(){
	}
	class B {
		public void add(){
		}
		public void bb(){
		}
	}
}

5.6.2静态内部类

创建方式:

  • 需要导包:
Inner02 inner02 = new Outer02.Inner02();
  • 不需要导包:
Outer02.Inner02 inner = new Outer02.Inner02();

静态内部类调用外部类的成员:创建外部类的对象。

外部类调用静态内部类的成员:创建对象,如果静态内部类中的成员是静态的,那么直接类名调用。

静态内部类实现单例模式天然线程安全、延时加载

class Single{
	private Single(){
	}
	public static Single getInstance(){
		return SingleHolder.single;
	}
	static class SingleHolder{
		private static Single single = new Single();
	}
}

实现原理:目前为止,我们所了解的加载类的方法有两种,一种是new对象的时候加载,一种是使用main方法。在之前了解的单列模式中,饿汉式时及时加载,懒汉式是延时加载,在懒汉式中,是通过调用方法时才加载类,而不是构造对象的时候加载,做到了延时加载。这里,当外部new一个Single对象时,调用getInstance()方法,此时才加载Single类,当代码执行到SingleHoleder.single时,才使用了SingleHoleder静态内部类,所以这个时候才会去执行SingleHolder类中的代码,初始化single属性时,new一个Single对象。并且只会初始化这一次。做到了天然单列。

5.6.3匿名内部类

匿名内部类也就是没有名字的内部类,正因为没有名字(在编程中,即为:没有引用),所以匿名内部类只能使用一次,它通常用来简化代码编写。但使用匿名内部类还有个前提条件:必须继承一个父类或实现一个接口

		jButton2.addMouseListener(new java.awt.event.MouseAdapter() {
			public void mouseClicked(java.awt.event.MouseEvent evt) {
				jButton2MouseClicked(evt);
			}
		});

5.6.4方法内部类

class Outer {
	public void doSomething(){
		final int a =10;
		class Inner{
			public void seeOuter(){
				System.out.println(a);
			}
		}
		Inner in = new Inner();
		in.seeOuter();
	}
	public static void main(String[] args) {
		Outer out = new Outer();
		out.doSomething();
	}
}

5.7数组

5.7.1数组的概念

在内存中连续存放的,数据类型单一的(能自动转换的也叫单一),长度固定的数据结构。

数组的下标是从0开始的,最后一个下标为数组的长度-1.

数据结构:数据在内存中存放的形式。(一堆苹果在房间按大小或者按编号一次排序)。算法是指在实行数据结构的手段(也就是苹果以什么手段来排序分类)。

数组的定义:

数据类型 [] [] 数组名称 =  new 数据类型 [长度] [长度]

     int    []     ary   =  new   int     [4]

前面的[]代表的是维度,有2个就是2维,3个就是3维,后面的[长度]也要对应,2维即是平面(表格),3维即是空间(xyz轴),4维即是加上时间。

在JAVA中一共有16个功能空间。数组存放在栈这个功能空间中,而变量则是存放在堆这个功能空间中。上面的数组的定义就是:先在堆中开辟一个4长度的空间,再在栈中开辟一个叫ary的空间,指向堆中的4长度空间。在没有给数组赋值时,不能使用,当要定义一个数组,但是还不知道怎么用时,可以先赋值一个null。

数组的本质是引用型,数组的使用不能越界,也就是下标不能超出范围,这个范围即是长度。在编程中我们通常写的是ary.length(数组的长度):for(int i = 0 ; i < ary.length ; i++)数组名.length在C中数组的长度只能是常量,c中的常量写法是名称全部大写。但是在java中数组的长度可以是变量。

5.7.2数组的存放与遍历:

public class ArrYhTriangle{
	public static void main(String[] args){
		System.out.println ("用二维数组,储存一个9行的杨辉三角,并打印出来。");
		int [][] yhTriangle = new int [9][9];
		int i = 0;
		for(i = 0; i < yhTriangle.length ; i++){
			//这里因为只需要对属于杨辉三角的位置的数赋值,所以j的循环次数随i的循环递增
			for(int j = 0 ; j <= i ; j++){
				if( j == 0 || j == i){
					yhTriangle[i][j] = 1;					
				}else {
					yhTriangle[i][j] = yhTriangle[i-1][j] + yhTriangle[i-1][j-1];
				}
			}
		}
		for(i = 0; i < yhTriangle.length ; i++){
			for(int j = 0 ; j < yhTriangle[i].length ; j++){
				//虽然我们理解的二维数组是表格,但是实际在内存中不是,所以如果依次打印的话,不会出现三角形
				//这里只需要打印出我们需要的数据,也就是j<=i时候的数
				if( j <= i ){
					System.out.print(yhTriangle[i][j]+"  ");
				}
			}
			System.out.println ();
		}
		System.out.println ();
	}
}

5.7.3数组的copy

import java.util.Scanner;
public class ArrCopy{
	public static void main(String[] args){
		Scanner sc = new Scanner(System.in);
		System.out.println ("请输入数组长度");
		int num = sc.nextInt();
		if(num < 1){
			System.out.println ("输入长度非法");
			return;
		}
		int [] ary1 = new int [num];
		int [] ary2 = new int [ary1.length];
		for(int i = 0 ; i < ary1.length ; i++){
			System.out.println ("请输入第"+(i+1)+"个数据");
			ary1[i] = sc.nextInt();
			ary2[i] = ary1[i];
		}
			System.out.println ("ary2在Copyary1的数据后是:");
		for(int i = 0 ; i < ary1.length ; i++){
			System.out.print (ary2[i]+"  ");
		}
	}
}

5.7.4多维数组的应用

import java.util.Scanner;
public class ArrSqu{
	public static void main(String[] args){
		Scanner sc = new Scanner(System.in);
		System.out.println ("请输入一个3*3矩形方阵的任意9个整数");
final int row = 3;
		int [][] squ = new int [row][row];
		//sum1是正对角线之和
		int sum1 = 0;
		int sum2 = 0;
		for(int i = 0 ; i < squ.length ; i++){
			for( int j = 0 ; j < squ[i].length ; j++){
				System.out.println ("请输入数组["+i+"]["+j+"]的值");
				int num = sc.nextInt();	
				squ [i][j] = num ;
				if( i == j){
					sum1 = sum1 + squ[i][j];
				}
			}
		}
		//反对角线之和				
		for(int i = 0 ,j = squ.length - 1  ; i < squ.length ; i++,j--){  
			sum2 = sum2 + squ[i][j];	
		}
		System.out.println ();
		System.out.println ("您输入的9个整数如下:");
		for(int i = 0 ; i < squ.length ; i++){
			for(int j = 0 ; j < squ[i].length ; j++){
				System.out.print (squ[i][j]+"  ");				
			}
			System.out.println ();
		}
		System.out.println ("正对角线之和为:"+sum1+",反对角线之和为:  "+sum2);	
	}
}

猜你喜欢

转载自blog.csdn.net/qq_40064948/article/details/81741076