您的位置:首页 > 职场人生

黑马程序员——Java基础—面向对象(一)

2015-04-02 19:56 183 查看
———Java培训、Android培训、iOS培训、.Net培训、期待与您交流! ———

一理解面向对象

二面向对象的特征

三类与对象的关系

四类的定义

五成员变量和局部变量的不同

六匿名对象

七封装

八构造方法

九构造代码块

十this关键字

十一static关键字

十二对象的初始化过程

十三设计模式之单例设计模式

面向对象(一)

一、理解面向对象

面向对象作为一种编程思想,其本质是基于面向过程的。相对于强调功能行为的面向过程变成方式而言,面向对象强调的是功能的封装,形成具备一定功能的对象。面向对象的思维方式,符合人们的思考习惯,可以将复杂的事情简单化。从面向过程到面向对象,程序员完成了从执行者到指挥者的角色转变。

在使用面向对象概念的时候:

1.先找具有所需要功能的对象是否存在,存在即可使用;

2.如果不存在,那么创建一个具备所需功能的对象;

3.创建具备不用功能对象的过程,就是简化开发,提高代码复用性的过程

二、面向对象的特征:

1.封装:Java的封装是一种信息隐藏技术,将属性和方法封装在一个类当中,只对调用者开放相应接口来访问该类的成员属性和方法。封装提高了程序的复用性和可维护性;

2.继承:Java的继承特性,能使一个派生类类拥有其父类的属性和方法,并扩展其特有的方法;

3.多态:指允许不同类的对象对同一方法做出不同响应。即同一方法可以根据调用对象的不同而表现出多种不同的行为方式。多态的发生必须满足1)继承;2)复写;3)父类引用指向子类对象;这三个条件

三、类与对象的关系

Java中通过定义类的形式,来描述生活中的具体事物。类是具体事物的抽象定义。对象则是该类在生活中的具体体现,是实实在在的个体。



四、类的定义

生活中描述事物,就是要描述事物的不同属性和行为。如:人有身高,体重,年龄等属性;有起床,刷牙等行为。在Java中,用类
(class)
来描述事物。

属性:就是类中声明的成员变量;

行为:就是类中声明的成员方法;

定义一个类,其实就是在定义类中的成员变量和成员方法。

类的示例代码:

class Car {
String color = "red";
int num = 4;

public void run() {
System.out.println("Running...");
}
}


创建对象的代码示例:

class Test {
public static void main(String[] args) {
Car c1 = new Car();
c1.color = "blue";
Car c2 = new Car();
}
}


该对象的内存结构图如下:



new
一个
Car
对象的时候:

1.在堆内存中开辟空间,储存
Car()
对象及其成员变量
color
num


2.将内存地址赋给栈内存中该对象的引用
c1
c1
即指向
Car()
对象;

3.
c1
修改
color
的值,将新值赋给
color


五、成员变量和局部变量的不同

1.作用域不同:

1)成员变量作用域为整个类;

2)局部变量作用域为方法或者语句

2.在内存中的位置不同:

1)成员变量在堆内存中;对象实例被new出来之后,才开辟内存空间给成员变量;

2)局部变量在栈内存中

六、匿名对象:

1.应用一:

new Car().num = 5;
new Car().color = "green";
new Car().run();


如果使用多个匿名对象,第一行执行完之后,由于没有引用指向这个对象,该对象立刻成为垃圾,等待回收。



用匿名对象调用方法是通常做法,调用成员属性没有意义。所以,当对对象方法只调用一次时,可以用匿名对象完成。如果对对象进行多个成员调用,必须给这个对象起名字。

2.应用二:

将匿名对象作为实际参数进行传递。

不使用匿名对象作为参数的示例代码:

class Test {
public static void main(String[] args) {
Car c = new Car();
show(c);
}

public static void show(Car c) {
c.num = 3;
c.color = "black";
c.run();
}
}


上述代码内存运行过程如图:



运行过程:

1.将创建的对象的内存地址赋给引用
c
c
即指向该对象;

2.将引用
c
传给该对象的
show()
方法,即将该对象的地址赋给
show()
方法的参数
c
show()
方法中的参数
c
即指向该对象;

3.
show()
方法访问该对象的成员变量

使用匿名对象作为参数的示例代码:

public static void main(String[] args) {
show(new Car());
}

public static void show(Car c) {
c.num = 3;
c.color = "black";
c.run();
}


上述代码内存运行过程如图:



使用匿名对象作为参数,直接将对象地址赋给
show()
方法的参数
c
c
再访问该对象的成员变量。

七、封装

1.定义

封装是指,隐藏对象的属性和实现细节,仅对外提供公共访问方式。

2.封装的好处

1)类的内部变化被隔离,调用者无须知道这些变化,照样能实现功能;

2)类内部细节的实现无须调用者了解,便于使用;

3)封装的代码可以被不同调用者使用,提高复用性;

4)不想对外开放的属性,通过封装能很好隐藏起来,提高安全性

3.private关键字

private:
用于修饰类中的成员变量和成员方法,被修饰的成员只能在本类被访问。
private
是封装的一种表现形式。当使用
private
修饰成员变量后,应设置相应的
get
set
方法,让外部访问私有变量。

示例代码:

class Person {
private String name;
private int age;

public void setName(String name) {
this.name = name;
}

public void setAge(int age) {
this.age = age;
}

public void getName() {
return name;
}

public void getAge() {
return age;
}
}


八、构造方法

1.构造方法特点

1)方法名与类名相同;

2)不用定义返回值类型;

3)不可以写
return
语句;

4)在创建对象的时候,自动调用

2.构造方法的作用

构造方法可以对对象进行初始化。对象在创建的时候,就应该具备其基本特性。构造方法就初始化了这些基本特性。

3.构造方法小细节

1)当一个类中没有定义构造方法时,Java默认会为该类加入一个空参数的构造方法;

2)当在类中声明了自定义的构造方法后,Java不再为该类添加构造方法;

3)对象创建时就具备的特性和行为,可以定义在构造函方法中,以便创建时初始化;

4)构造方法中初始化过了的私有变量,还是需要定义
set
get
方法,因为在对象的使用过程中,可能涉及改变其私有变量的值,此时就要用到
set
方法,获取该私有变量的值,需要用到get方法

示例代码:

class Person {
private String name;
private int age;

public void setName(String name) {
this.name = name;
}

public void setAge(int age) {
this.age = age;
}

public void getName() {
return name;
}

public void getAge() {
return age;
}

Person(String name, int age) {
this.name = name;
this.age = age;
}
}

class Test {
public static void main(String[] args) {
Person p = new Person("Lucy", 20);
p.setName("Linda");
}
}


九、构造代码块

1.构造代码块的定义

构造代码块是定义在类中的一个独立代码区间。构造代码块的作用是为对象进行初始化。对象一旦创建就立刻运行构造代码块,且运行优先级高于该类的构造方法。

2.构造代码和构造方法的区别

1)构造代码是给所有对象进行统一初始化;

2)构造方法是给对应的对象初始化

3.构造代码块的应用

将所有对象的共性属性或行为定义在构造代码块中,那么所有对象创建的时候同一执行构造代码块中的内容。

示例代码:

class Person {
private String name;
private int age;

{
this.name = "Travis";
this.age = 20;
}

Person(String name, int age) {
this.name = name;
this.age = age;
}
}


十、this关键字

1.this的概念

this
关键字代表调用
this
所在方法的对象的引用,即哪个对象调用了
this
所在的方法,
this
就指向那个对象。

2.this关键字的应用

1)
this
关键字用于区分局部变量和成员变量重名的情况;并且,定义类中方法时,如果需要在方法内部使用本类对象的时候,用
this
指向这个对象

示例代码:

package test;

public class Animal {
private String type;
private int leg;

Animal(String type, int leg) {
this.type = type; //this区分了成员变量和局部变量,并代表本类的对象
this.leg = leg;
}
}


2)this关键字可以用于构造方法间互相调用

示例代码:

package test;

public class Animal {
private String type;
private int leg;

Animal(String type) {
this.type = type; //this区分了成员变量和局部变量,并代表本类的对象
}

Animal(String type, int leg) {
this(type); //this关键字以type为参数调用了第一个构造函数
this.leg = leg;
}
}


3)注意:
this
关键字调用其他构造方法的语句只能写在构造方法的第一行,因为初始化动作要先执行

十一、static关键字

1.static关键字基本用法

static
关键字是一个修饰符,用于修饰成员变量和成员方法。被
static
修饰的成员,称为静态成员,或类成员。当一个成员是所有对象所共享的时候,用
static
关键字修饰,那么每次创建对象的时候,该成员就不会被初始化在堆内存中。

class Person {
String name;
static String country = "CN";
}


上述代码中,country被声明成静态变量,则该变量不再存在于堆内存中,而作为共享数据储存于方法区,如下图:



2.static关键字的特点

1)随着类的加载而加载

类一旦加载到内存,
static
成员就已经在内存中被分配空间。同时,
static
成员随着类的消失而消失,也就是说,
static
成员的生命周期最长

2)优先于对象存在

静态随着类的加载而存在,对象创建后才存在

3)被所有对象共享

所有对象共享一份静态成员

4)可以直接被类名所调用

由于类刚加载的时候,还没有对象,而静态成员已经加载到内存,所以可以用类名直接调用

3.静态变量和成员变量的区别

1)存放位置不同

静态变量随着类的加载而存在与方法区中

成员变量随着对象的创建而存在于堆内存中

2)生命周期不同

静态变量生命周期等于类的生命周期,随着类存在消亡

成员变量生命周期随着对象存在消亡,生命周期较静态变量短

4.static关键字注意事项

不能把所有成员都定义成
static
,因为:

1)不是所有成员都能被所有对象共享;

2)定义成
static
的成员,生命周期随着类存在消亡,对象使用完毕,这些成员还存在于内存,浪费资源;

3)静态方法中不能引用非静态成员,即静态方法只能访问静态成员(非静态方法可以访问静态成员);

4)静态方法中不能出现
this
super
关键字

5.静态的利弊

利:

1)存储所有对象共享的成员于方法区,节省资源;

2)可以直接被类名调用,使用简单

弊:

1)生命周期过长,可能出现浪费资源的情况;

2)访问出现局限性(静态方法只能访问静态成员)

6.使用static关键字的时机

1)当对象中出现需要共享的数据时,使用
static
关键字。这里的数据,指的是成员的值或功能,不是成员本身。成员本身称为共享的属性,而不是共享的数据,如
name
成员变量,是
Person
类的对象的共享属性,而非共享数据;

2)当成员方法没有对类中任何非静态成员变量进行操作的时候,该方法可以定义成静态成员方法

7.静态的应用

定义了
ArrayTools
类,包含各种操作数组的方法。由于所有的方法都没有操作该类中的成员变量,根据
static
关键字使用原则,声明该类中所有方法为静态成员方法。

示例代码:

package test;

/**
* 利用ArrayTools类实现冒泡排序,选择排序,普通查找和二分查找
* @author jeremy
*
*/

public class TestArrayTools {
public static void main(String[] args) {
int[] arr = new int[]{3, 98, 56, 4, 10, 66, 27, 17};

//冒泡排序
System.out.println("Bubble Sort:");
ArrayTools.bubbleSort(arr);
ArrayTools.printArray(arr);
System.out.println("----------------------------------------------");

//选择排序
System.out.println("Select Sort:");
ArrayTools.selectSort(arr);
ArrayTools.printArray(arr);
System.out.println("----------------------------------------------");

//选择排序2
System.out.println("Select Sort Version 2:");
ArrayTools.selectSort_2(arr);
ArrayTools.printArray(arr);
System.out.println("----------------------------------------------");

//普通查找
System.out.println("Search array for 10:");
System.out.println(ArrayTools.search(arr, 10));
System.out.println("----------------------------------------------");

System.out.println("Search array for 11:");
System.out.println(ArrayTools.search(arr, 11));
System.out.println("----------------------------------------------");

//二分查找
System.out.println("Half Search array for 27:");
System.out.println(ArrayTools.halfSearch(arr, 27));
System.out.println("----------------------------------------------");

System.out.println("Half Search array for 99:");
System.out.println(ArrayTools.halfSearch(arr, 99));
System.out.println("----------------------------------------------");

//二分查找2
System.out.println("Half Search array Version 2 for 56:");
System.out.println(ArrayTools.halfSearch(arr, 56));
System.out.println("----------------------------------------------");

System.out.println("Half Search array Version 2 for -9:");
System.out.println(ArrayTools.halfSearch(arr, -9));
System.out.println("----------------------------------------------");

}
}

class ArrayTools {

/**
* 交换数组中两个值的位置
* @param arr 接受数组作为第一参数
* @param a 接受整型数据作为第二参数,用于标识数组元素
* @param b 接受整型数据作为第三参数,用于标识数组元素
*/
private static void swap(int[] arr, int a, int b) {
int tmp = arr[b];
arr[b] = arr[a];
arr[a] = tmp;
}

/**
* 打印数组的方法
* @param arr 接受数组作为参数
*/
public static void printArray(int[] arr) {
System.out.print("[");
for(int x = 0; x < arr.length; x ++) {
if(x != arr.length - 1) {
System.out.print(arr[x] + ", ");
} else {
System.out.println(arr[x] + "]");
}
}
}

/**
* 冒泡排序,相邻两书比较,小的数左移
* @param arr 接受数组作为参数
*/
public static void bubbleSort(int[] arr) {
for(int x = 0; x < arr.length; x ++) {
for(int y = 0; y < arr.length - x - 1; y ++) {
if(arr[y] > arr[y + 1]) {
swap(arr, y, y + 1);
}
}
}
}

/**
* 选择排序一,前一个数和后一个做比较,若大于后一个数,则立刻交换位置
* @param arr 接受数组作为参数
*/
public static void selectSort(int[] arr) {
for(int x = 0;x < arr.length - 1; x ++) {
for(int y = x + 1; y < arr.length; y ++) {
if(arr[x] > arr[y]) {
swap(arr, x, y);
}
}
}
}

/**
* 选择排序二,前一个数和后一个做比较,如果大于后一个,则交换下标值,让小的数继续比较
* 如果较小数的下标发生过变化,则和原数交换位置
* @param arr 接受数组作为参数
*/
public static void selectSort_2(int[] arr) {
int k;
for(int x = 0; x < arr.length - 1; x ++) {
k = x;
for(int y = x + 1; y < arr.length; y ++) {
if(arr[k] > arr[y]) {
k = y;
}
}
if(k != x) {
int tmp = arr[k];
arr[k] = arr[x];
arr[x] = tmp;
}
}
}

/**
* 普通查找,遍历数组,将每个元素和目标值作比较
* @param arr 接受数组作为第一参数
* @param key 接受整型目标值作为第二参数
* @return 匹配成功,返回下标;匹配失败,返回-1
*/
public static int search(int[] arr, int key) {
for(int x = 0; x < arr.length; x ++) {
if(arr[x] == key) {
return x;
}
}
return -1;
}

/**
* 二分法查找一
* @param arr 接受数组作为第一参数
* @param key 接受整型目标值作为第二参数
* @return 匹配成功,返回下标;匹配失败,返回-1
*/
public static int halfSearch(int[] arr, int key) {
int min = 0;
int max = arr.length - 1;
int mid = (min + max) / 2;

while(arr[mid] != key) {
if(arr[mid] < key) {
min = mid + 1;
} else {
max = mid - 1;
}
if(min > max) {
return -1;
}
mid = (min + max) / 2;
}
return mid;
}

/**
* 二分法查找二
* @param arr 接受数组作为第一参数
* @param key 接受整型目标值作为第二参数
* @return 匹配成功,返回下标;匹配失败,返回-1
*/
public static int halfSearch_2(int[] arr, int key) {
int min = 0;
int max = arr.length - 1;
int mid = (min + max) / 2;

while(min <= max) {
if(arr[mid] < key) {
min = mid + 1;
} else if(arr[mid] > key) {
max = mid - 1;
} else {
return mid;
}
}
return -1;
}
}


8.静态代码块

1)格式

static {
statement;
}


2)特点

随着类的加载而执行,并且只执行一次。用于对类进行初始化。

示例代码:

class TestStatic {

TestStatic() {
System.out.print("b ");
}

static {
System.out.print("a ");
}

{
System.out.print("c ");
}

TestStatic(int x) {
System.out.print("d ");
}
}

class TestStaticMain {
public static void main(String[] args) {
new TestStatic(5);
}
}


上述代码的输出结果为:a c d

执行过程为:

1> 先执行
static
代码块,由于其要初始化类,优先级最高,输出
a


2> 后执行构造代码块,由于其要初始化对象,优先级介于构造方法和静态代码块间,输出
c


3> 最后执行相应的构造方法,输出
d


注意:

静态代码块中不能访问非静态成员变量,构造代码块可以访问非静态变量。

示例代码:

class TestStatic {

int num = 9;

static {
//错误,静态代码块先于非静态成员存在,无法访问非静态成员
System.out.print("a " + num);
}

{
//正确,构造代码块初始化本类对象,可以访问该类的成员变量
System.out.print("c " + num);
}
}

class TestStaticMain {
public static void main(String[] args) {
new TestStatic();
}
}


输出:c9

9.主函数的属性

主函数的定义:

主函数是一个特殊函数,作为程序的入口,被jvm调用。主函数格式固定。

public:主函数无访问限制

static:主函数为静态,随着类的加载就存在于方法区

void:主函数没有返回值

mian:不是关键字,是能被jvm识别的特殊标识符

String[] args:函数的参数,参数类型为字符串数组

十二、对象的初始化过程

1.对象的初始化过程如下:



1)通过虚拟机,加载类的
class
文件到内存;

2)执行方法区静态代码块(如有),对类进行初始化;

3)在堆内存中开辟对象
new Person()
,及其类成员变量的储存空间,并对成员变量进行初始化;在栈内存中开辟
main
方法和该对象引用的储存空间;

4)对类成员变量进行显式初始化(如有);

5)执行构造代码块(如有),对对象进行初始化;

6)执行构造方法,调用相应的构造方法对成员变量进行初始化;

7)将内存地址赋给栈内存中该对象的引用

2.对象调用非静态成员方法过程

1)从方法区将要调用的非静态方法加载到栈内存;

2)将该对象的引用的内存地址,赋给要调用的方法中自带的this关键字,此时,this指代的就是该对象的引用;

3)内存地址赋值完毕,this指向该对象,将栈内存中变量的值,赋给堆内存中对象的成员变量,完成赋值,结束对非静态方法的调用

十三、设计模式之单例设计模式

设计模式是解决某一类问题最行之有效的方法。作为Java23种设计模式中的一种,单例设计模式解决了一个类在内存中只能存在一个对象的问题,即保证一个类,在内存中只有一个对象。

想要保证对象唯一性:

1.禁止其他程序建立该类的对象;

2.在该类中自定义一个对象;

3.开放端口,让其他程序访问该对象

实现:

1.私有化构造方法;

2.在该类中创建一个本来对象;

3.提供方法获取该对象

饿汉式:

class Single {

//私有化构造方法,其他程序不能创建该类对象
private Single() {}

//在本类中,创建静态的本类对象
private static Single s = new Single();

//对其他程序提供静态方法,获取该类对象
public static Single getInstance() {
return s;
}
}

class TestSingle {
public static void main(String[] args) {
//调用Single类的静态方法,获取Single对象
Single s = Single.getInstance();
}
}


单例模式内存图:



图示说明:

1.
Single
类在加载的时候,其静态成员变量
s
,静态成员函数
getInstance()
以及构造函数都已加载到方法区;在
Single
类中创建
Single
对象的时候,已经将
Single
对象的内存地址赋给方法区中的静态变量
s


2.
mian()
方法中调用了
getInstance()
方法后,返回的
Single
对象
s
Single
对象的内存地址赋给栈内存中的变量
s
,即栈内存中的变量
s
现在指向堆内存中的
Single
对象,是
Single
对象的引用;

3.由于
Single
类的构造方法被声明为私有,则外部程序无法创建该类的对象;若有代码
Single s1 = Single.getInstance();
则继续执行上述两个步骤,结果是
s1
仍然指向堆内存中的同一
Single
对象,这保证了
Single
对象的唯一性

懒汉式:

class Single {

//私有化构造方法,其他程序不能创建该类对象
private Single() {}

//延迟创建本类对象
private static Single s = null;

//对其他程序提供静态方法,获取该类对象
public static Single getInstance() {
//判断,如果s为空,则创建本类对象
if(s == null) {
s = new Single();
}
//返回该对象
return s;
}
}


饿汉式和懒汉式的不同:

饿汉式:
Single
类一旦加载到内存,就马上创建对象

懒汉式:
Single
类加载到内存时,对象没有被创建。只有调用了
getInstance()
方法时,才创建对象

开发时,用饿汉式

———Java培训、Android培训、iOS培训、.Net培训、期待与您交流! ———
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: