学习面向对象内容的三条主线
1.Java类及类的成员
2.面向对象的三大特征
3.其他关键字
一.面向过程与面向对象
面向过程(POP)与面向对象(OOP)
二者都是一种思想,面向对象是相对于面向过程而言的。面向过程,强调的是功能行为,以最小函数为最小单位,考虑怎么做。面向对象,将功能分装进对象,强调具备了功能的对象,以类/对象为最小单位,考虑谁来做。
面向对象更加强调运用人类在日常的思维逻辑中采用的思想方法与原则,如抽象,分类,继承,聚合,多态等。
面向对象的三大特征
封装
继承
多态
二.Java语言的基本元素:类和对象
类(Class)和对象(Object)是面向对象的核心概念。
类是对一类事物的描述,是抽象的,概念上的定义。
对象是实际存在的该类事物的每个个体,因而也称为实例(instance)。
万事万物皆对象
Java类及类的成员
常见的成员有
属性:对应类中的成员变量。
行为:对应类中的成员方法。
Field = 属性 = 成员变量,Method = (成员)方法 = 函数。
创建Java自定义类
步骤:
1.定义类(考虑修饰符,类名)
2.编写类的属性(考虑修饰符、属性类型、属性名、初始化值)
3.编写类的方法(考虑修饰符、返回值类型、方法名、形参等)
三.对象的创建和使用
错误的写法
'com.atguigu.test.test1. this' cannot be referenced from a static context
这句话的意思是:“com.atguigu.test.test1。这个“不能从静态上下文中引用”
Class 'Animal' is public, should be declared in a file named 'Animal.java'
这句话的意思是类Animal是公共的,应该在名为Animal.java的文件中声明
所以我们要将public去掉,这才是正确的写法。
这时test1下会有两个类
正确的写法
此时应当在同一个包下创建一个Animal的类
由此,我们可以得到最后的结果。
类的访问机制:
在一个类中的访问机制:类中的方法可以直接访问类中的成员变量。
(例外:static方法访问非static,编译不通过)
在不同类中的访问机制:先创建要访问类的对象,再用对象访问类中定义的成员。
堆(Heap):此内存区域的唯一目的就是存放对象实例,几乎所有的对象实例都在这里分配内存。这一点在Java虚拟机规范中的描述是:所有的对象实例以及数据都要在堆上分配。
通常所说的栈(stack),是指虚拟机栈。虚拟机栈用于存储局部变量等。局部变量表存放了编译期可知长度的各种基本数据类型(boolean,byte,char,short,int,float,long,double),对象引用(reference类型,他不等同于对象本身,是对象在堆内存的首地址)。方法执行完,自动释放。
方法区(Method Area),用于存储已被虚拟机加载的类信息,常量,静态变量,即时编译器编译后的代码等数据。
对象的创建和使用:匿名对象
我们也可以不定义对象的句柄,而直接调用这个对象的方法。这样的对象叫做匿名对象。
如:new Person().shout();
使用情况
如果对一个对象只需要进行一次方法调用,那么就可以使用匿名对象。
我们经常将匿名对象作为实参传递给一个方法调用.
4.4类的成员之一:属性(field)
语法格式:
修饰符 数据类型 属性名 = 初始化值;
说明1:修饰符
常用的权限修饰符有:private、缺省、protected、public
其他的修饰符:static、final(暂不考虑)
说明2:数据类型
任何基本数据类型(如int、Boolean)或任何引用引用数据类型。
说明3:属性名
属于标识符,符合命名规则和规范即可。
4.4类的成员之一:属性
对象属性的默认初始化赋值
当一个对象被创建时,会对其中各种类型的成员变量自动进行初始化赋值。除了基本数据类型之外的变量类型都是引用类型。
五.类的成员之二:方法(method)
什么是方法(method、函数):
方法是类或对象行为特征的抽象,用来完成某个功能的操作。在某些语言中也称为函数或过程。
将功能封装为方法的目的是,可以实现代码重用,简化代码。
Java里的方法不能独立存在,所有的方法必须定义在类里。
方法的分类:按照是否有形参及返回值
方法的调用
方法通过方法名被调用,且只有被调用才会执行。
注意:
方法被调用一次,就会执行一次。
没有具体返回值的情况,返回值类型用关键字void表示,那么方法体中可以不必使用return语句。如果使用,仅用来结束方法。
定义方法时,方法的结果应该返回给调用者,交由调用者处理。
方法中只能调用方法或属性,不可以再方法内部定义方法。
自定义的数组工具类
/*
自定义数组的工具类
*/
public class ArrayUtil {
//求数组的最大值
public int getMax(int[] arr){
int maxValue = arr[0];
for(int i = 1;i < arr.length;i++){
if(maxValue < arr[i]){
maxValue = arr[i];
}
}
return maxValue;
}
//求数组的最小值
public int getMin(int[] arr){
int minValue = arr[0];
for(int i = 1;i < arr.length;i++){
if(minValue > arr[i]){
minValue = arr[i];
}
}
return minValue;
}
//求数组之和
public int getSum(int[] arr){
int sum = 0;
for(int i = 0;i < arr.length;i++){
sum += arr[i];
}
return sum;
}
//求数组平均值
public int getAverage(int[] arr){
int average = 0;
for(int i = 0;i < arr.length;i++){
average += arr[i];
}
return (average/arr.length);
}
//反转数组
public void reverse(int[] arr){
for(int i = 0;i < arr.length;i++){
int temp = arr[i];
arr[i] = arr[arr.length - i - 1];
arr[arr.length - i - 1] = temp;
}
}
//复制数组
public int[] copy(int arr[]){
int[] arr1 = new int[arr.length];
for(int i = 0;i < arr.length;i++){
arr1[1] = arr[i];
}
return arr1;
}
//数组排序:冒泡排序
public void sort(int[] arr){
for(int i = 0;i < arr.length;i++){
for(int j = 0;j < arr.length - i - 1;j++){
if(arr[j] > arr[j + 1]){
int temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
}
}
//交换数组中两个指定元素的位置
public void swap(int[] arr,int i,int j){
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
//遍历数组
public void print(int[] arr){
for(int i = 0;i < arr.length;i++){
System.out.print(arr[i] + " ");
}
System.out.println();
}
//查找指定元素:二分排序
public int getIndex(int[] arr, int dest) {
// 线性查找:
for (int i = 0; i < arr.length; i++) {
if (dest == arr[i]) {
return i;
}
}
return -1;//返回一个负数,表示没有找到
}
}
对象数组的例子:
//4. 对象数组题目:
// 定义类Student,包含三个属性:学号number(int),年级state(int),成绩
// score(int)。 创建20个学生对象,学号为1到20,年级和成绩都由随机数确定。
// 问题一:打印出3年级(state值为3)的学生信息。
// 问题二:使用冒泡排序按学生成绩排序,并遍历所有学生信息
// 提示:
// 1) 生成随机数:Math.random(),返回值类型double;
// 2) 四舍五入取整:Math.round(double d),返回值类型long。
public class PersonTest {
public static void main(String[] args){
//创建学生长度为20数组:
Student[] students = new Student[20];
for(int i = 0;i < students.length;i++){
//创建数组的单个对象,这里循环创建20个学生对象
students[i] = new Student();
//为每个学生的学号(number)赋值 1 - 20
students[i].number = i + 1;
//为每个学生的年级赋值 年级[1,6]
//这里的 乘法表示在[0,1]的范围内乘6 +1 是给括号里左边的数加1的,不会分配到右边
students[i].state = (int)(Math.random() * 6 + 1);
//为每个学生的成绩赋值 成绩[0,100]
students[i].score = (int)(Math.random() * 100);
}
for(int i = 0;i < students.length;i++){
System.out.println("学生:" + students[i].number + " 的年级为:" + students[i].state +
" 的成绩为:" + students[i].score);
}
PersonTest personTest = new PersonTest();
personTest.searchStudentByNumber(students,3);
personTest.searchStudentByState(students,1);
personTest.bubbleSort(students);
for(int i = 0;i < students.length;i++){
System.out.println("学生:" + students[i].number + " 的年级为:" + students[i].state +
" 的成绩为:" + students[i].score);
}
}
//按照学号查找相应学生的信息
public void searchStudentByNumber(Student[] students,int number){
for(int i = 0;i < students.length;i++){
if(number == students[i].number){
System.out.println("学生的学号为;" + students[i].number + " 学生的年级为:" +
students[i].state + " 学生的成绩为:" + students[i].score);
}
}
}
//按照年级来查找学生的信息(多个)
public void searchStudentByState(Student[] students,int state){
for(int i = 0;i < students.length;i++){
if(state == students[i].state){
System.out.println("学生的学号为;" + students[i].number + " 学生的年级为:" +
students[i].state + " 学生的成绩为:" + students[i].score);
}
}
}
//冒泡排序法对学上的成绩进行排序
public void bubbleSort(Student[] students){
for(int i = 0;i < students.length;i++){
for(int j = 0; j < students.length -i - 1;j++){
if(students[j].score > students[j + 1].score){
int temp = students[j].score;
students[j].score = students[j + 1].score;
students[j + 1].score = temp;
}
}
}
}
}
class Student{
public int number; //学号
public int state; //年级
public int score; //成绩
}
4-6 再谈方法
4.6.1方法的重载
4.6.2可变形参的方法
4.6.3方法参数的值传递机制
4.6.3递归方法
方法的重载
重载的概念
在同一个类中,允许存在一个以上的同名方法,只要他们的参数个数或者参数类型不同即可。
重载的特点:
与返回值类型无关,只看参数列表,且参数列表必须不同。(参数个数或参数类型)。调用时,根据方法参数列表的不同来区别。
重载示例:
//返回两个整数的和
int add(int x,int y){return x + y};
//返回三个这个整数的和
int add(int x,int y,int z){return x + y + z};
//返回两个小数的和
double add(double x,double y){return x + y};
4.6 再谈方法2:可变个数的形参
JavaSE5.0中提供了Varargs(variable number of arguments)机制,允许直接定义和多个实参相匹配的形参,从而,可以用一种更简单的方式,来传递个数可变的实参。
//JDK5.0之前:采用数组形参来定义方法,传入多个同一类型变量。
public static void test(int a,String[] books);
//JDD5.0:采用可变个数形参来定义方法,传入多个同一类型变量
public static void test(int a,String...books);
说明:
1.声明格式:方法名(参数的类型名...参数名)
2.可变参数:方法参数部分指定类型的参数个数时可变多个:0个,1个或多个。
3.可变个数形参的方法与同名的方法之间,彼此构成重载。
4.可变参数方法的使用与方法参数部分使用数组是一致的。
5.方法的参数部分有可变形参,需要放在形参声明的最后。
6.在一个方法的形参位置,最多只能声明一个可变个数形参。
上图二者的意思是一样的,不能这样写,构不成重载。
可变形参的例子:
public class test2 {
public static void main(String[] args){
test2 to = new test2();
//下面两次调用将执行第二个test方法
to.test1();
to.test1("aa","bb");
//下面将执行第一个test方法
to.test(new String[]{"aa"});
}
public void test(String[] msg){
System.out.println("含字符串数组参数的test方法");
}
public void test1(String book){
System.out.println("****与可变形参方法构成重载的test1方法****");
}
public void test1(String...books){
System.out.println("****形参长度可变的test1方法****");
}
}
4.6 再谈方法3:方法参数的值传递机制
方法,必须由其所在类或对象调用才有意义。若方法含有参数:
形参:方法声明时的参数。
实参:方法调用时实际传给形参的参数值。
Java的实参值如何传入方法呢?
Java里方法的函数传递方式只有一种:值传递。即将实际参数的副本(复制品)传入方法内,而参数本身不受印象。
形参是基本数据类型:将实参基本数据类型变量的“数据值”传递给形参。
形参是引用数据类型:将实参引用数据类型变量的“地址值”传递给形参。
public class test3 {
public static void main(String[] args) {
DataSwap ds = new DataSwap();
ds.a = 5;
ds.b = 10;
swap(ds);
System.out.println("交换结束后,a Field的值是" + ds.a + ";b Field的值是" + ds.b);
}
public static void swap(DataSwap ds){
int temp = ds.a;
ds.a = ds.b;
ds.b = temp;
System.out.println("swap方法里,a Field的值是" + ds.a + ";b Field的值是" + ds.b);
}
}
class DataSwap{
public int a;
public int b;
}
代码举例:
//定义一个类PassObject,在类中定义一个方法printAreas(),该方法的定义
// 如下:public void printAreas(Circle c, int time)
// 在printAreas方法中打印输出1到time之间的每个整数半径值,以及对应的面积。
// 例如,times为5,则输出半径1,2,3,4,5,以及对应的圆面积。
public class test4 {
public static void main(String[] args){
Circle circle = new Circle();
PassObject passObject = new PassObject();
passObject.printAreas(circle,5);
}
}
class PassObject{
public void printAreas(Circle c,int time){
for(int i = 1;i <= time;i++){
System.out.println(i + " " + c.findArea(i));
}
}
}
class Circle{
double radius;
public double findArea(double radius){
return radius * radius * 3.14;
}
}
4.6再谈方法4:递归(recursion)方法
递归方法:一个方法体内调用它自身
方法递归包含了一种隐式的循环,他会重复执行某段代码,但这种重复执行无需循环控制。
递归一定要相已知方向递归,否则这种递归就变成了无穷递归,类似于死循环。
//请用Java写出递归求阶乘(n!)的算法
public class test5 {
public static void main(String[] args){
test5 t = new test5();
System.out.println(t.factorial(5));
}
public int factorial(int num){
if(num == 1){
return num = 1;
}else{
return num = num * factorial(num - 1);
}
}
}
七.面向对象的特征之一:封装与隐藏
我们程序设计追求“高内聚,低耦合”。
高内聚:类的内部数据操作细节自己完成,不允许外部干涉。
低耦合:进对外暴露少量的方法用于使用。
隐藏对象内部的复杂性,只对外公开简答的接口。便于外界调用,从而提高系统的可扩展性,可维护性,通俗的说,把该隐藏的隐藏起来,该暴露的暴露出来,这就是封装兴性的设计思想。
使用者对类内部定义的属性(对象的成员变量)的直接操作会导致数据的错误、混乱或安全性问题。
信息的封装和隐藏
Java中通过将数据声明为私有的(private),再提供公共的(public)方法:getXxx()和setXxx()实现对该属性的操作,以实现下述目的:
隐藏一个类中不需要对外提供的实现细节。
使用者只能通过事先制定好的方法来访问数据,可以方便地加入控制逻辑,限制对属性的不合理操作。
便于修改,增强代码的可维护性。
四种访问权限修饰符
Java权限修饰符public,protected、(缺省)、private置于类的成员定义前,用来限定对象对该类成员的权限访问。
对于class的权限修饰只可以用public和default(缺省)
public类可以在任意地方被访问。
default类只可以被同一个包内部的类访问。
八.类的成员之三:构造器(或构造器方法)
构造器的特征:
它具有与类相同的名称
他不具备返回值类型。(与声明为void不同)
不能被static、final、synchronized、abstart、native修饰,不能有return语句返回值。
构造器的作用:创建对象;给对象进行初始化
如:Order o = new Order(); Person p = new PersonO("Peter",15);
根据参数不同,构造器可以分为如下两类
隐式无参构造器(系统默认提供)
显式定义一个或多个构造器(无参、有参)
注意:
Java语言中,每个类都至少有一个构造器
默认构造器的修饰符与所属类的修饰符一致
一旦显示定义了构造器,则系统不再提供默认构造器
一个类可以创建多个重载的构造器
父类的构造器不可被子类继承
构造器重载
总结:属性赋值过程
截止到目前,我们讲到了很多位置都可以对类的属性赋值。现总结这几个位置,并指明赋值的先后顺序。
默认的赋值位置
① 默认初始化
② 显示初始化
③ 构造器中初始化
④ 通过’对象.属性‘ 或“对象.方法”的方式赋值
赋值的先后顺序:
① - ② - ③ - ④
拓展知识 JavaBean
JavaBean是一种Java语言写成的可重用组件。
所谓javaBean,是指符合如下标准的Java类:
类是公共的
有一个无参的公共的构造器
有属性,且有对应的get、set方法
用户可以使用JavaBean将功能、处理、值、数据库访问和其他任何可以用Java代码创造的对象进行打包,并且其他的开发者可以通过内部的JSP页面,Servlet、其他JavaBean、applet程序或应用来使用这些对象。用户可以认为JavaBean提供了一种随时随地的赋值和粘贴功能,而不用关心任何改变。
拓展知识:UML类图
九.关键字:this的使用
this是什么?
在Java中,this关键字比较难理解,他的作用和其词义很接近。
他在方法内部使用,即这个方法所属对象的引用。
他在构造器内部使用,表示该构造器正在初始化的对象。
this可以调用类的属性,方法和构造器。
什么时候使用this关键字呢?
当方法内需要用到调用该方法的对象是,就用this。
具体的,我们可以使用this区分属性和局部变量。
比如:this.name = name;
注意:
可以在类的构造器用this(形参列表)的方式,调用本类中重载的其他构造器
明确:构造器中不能通过this(形参列表)的方式调用自身构造器
如果一个类中声明了n个构造器,则最多有n - 1个构造器中使用了this(形参列表)
this(形参列表)必须声明在类的构造器首行。
在类的一个构造器中,最多只能声明一个this(形参列表)
十.关键字:package、import的使用
package语句作为Java源文件的第一条语句,指明该文件中定义的类所在的包。(若缺省该语句,则指定为无名包)。它的格式为:
package 顶层包名.子报名;
包对应于文件系统的目录,package语句中,用"."来指明包(目录)的层次;
包通常用小写单词标识。通常使用所在公司域名的倒置:com.atguigu.xxx
包的作用:
包帮助管理大型软件系统,将功能相近的类划分到同一个包中,比如:MVC的设计模式
包可以包含类的子包,划分项目层次,便于管理
解决类命名冲突的问题
控制访问权限。
关键字:import
为使用定义在不同包中的Java类,需用import语句来引入指定包层次下所需的类或全部类(.*).import语句告诉编译器到哪区寻找类。
语法格式:
import 包名.类名