Java面向对象编程
java面向对象学习的三条主线:
- Java类和类的成员:属性Field、方法Method、构造器Constractor;代码块、内部类
- 面向对象三大特征:封装性、继承性、多态性、(抽象性)
- 其他关键字:this、super、static、final、abstract、interface、package、import等
面向对象OOP与面向过程POP对比:
-
面向过程:强调的是功能行为,以函数为最小单位,考虑怎么做。
①打开冰箱
②抬起大象,放进冰箱
③关上冰箱门
-
面向对象:强调具备了功能的对象,以类/对象为最小单位,考虑谁来做。
人{
打开(冰箱){
冰箱.打开();
}
抬起(大象){
大象.进入(冰箱);
}
关闭(冰箱){}
}
冰箱{
打开(){}
关闭(){}
}
大象{
进入(冰箱){}
}
1 类和对象
类是对一类事务的描述,是抽象的、概念上的定义
对象是实际的该类事务的每个个体,因此也成为实例(instance)
面向对象设计的重点是类的设计。
类的设计就是类的成员的设计。
- 属性(field):对应类中的成员变量、域、字段
- 行为:对应类中的成员方法(method)、函数
①类和对象的使用
- 创建类,设计类的成员
- 创建类的对象
- 通过“对象.属性”或“对象.方法”调用对象的结构
-
如果创建了一个类的多个对象,则每个对象都独立的拥有一套类的属性(非static的);意味着,如果我们修改一个对象的属性a,则不影响另外一个对象属性a的值。
-
对象内存解析
- 堆(Heap):存放对象实体
- 栈(Stack):存储局部变量(方法中的变量都是局部变量)
- 方法区(Method Area):类信息、常量、静态变量、即时编译器编译后的代码
②匿名对象
对于不定义对象的句柄,直接调用这个对象的方法。这样的对象就叫做匿名对象。
比如:System.out.println(new Person().shout()) 直接输出新new的对象的方法
System.out.println((new Person().name)) 直接输出新new的对象的属性(默认name=null,也可以使用带有参数的构造器进行赋值:System.out.println(new Person(“jiwei”, 24)))也可以输出属性。
③类中属性的使用:
属性(成员变量) VS 局部变量
-
相同点:
- 1 定义变量的格式:数据类型 变量名 = 变量值
- 2 先声明,后使用
- 3 变量都有其作用域
-
不同点:
-
1 在类中声明的位置不同
属性:直接定义在类的一对{}内
局部变量:声明在方法内、方法形参、代码块内、构造器形参、构造器内部的变量
-
2 关于权限修饰符的不同
属性:可以在声明属性的时候,指明其权限,使用public、private、缺省、protected等权限修饰符
局部变量:不可以使用权限修饰符
-
3 默认初始化值的情况
属性:根据其类型都有默认初始化值:整型(byte short int long)0、浮点型(float double)0.0、字符型(char)0、引用数据类型(类 数组 接口)null
局部变量:没有默认初始化值,意味着调用局部变量时一定显示赋值,形参在调用时赋值即可。
-
4 在内存中加载的位置不同
属性:加载的到堆空间(非static)
局部变量:加载到栈空间
-
成员变量(属性)和局部变量的内存解析:
对象属性的默认初始化值:
④类中方法的使用:
方法描述类应该有的功能。
分类:
无返回值 | 有返回值 | |
---|---|---|
无形参 | void 方法名(){} | 返回值类型 方法名(){} |
有形参 | void 方法名(形参列表){} | 返回值类型 方法名(形参列表){} |
-
方法的声明:权限修饰符 返回值类型 方法名(形参列表){
方法体}
-
关于权限修饰符:private 、public 、缺省、 protected
-
关于返回值类型:有返回值的、没返回值的
有返回值的:需要return关键字返回指定类型的变量或常量,一定需要返回
无返回值的:声明时需要使用void来表示,如果用“return”是结束方法的作用
-
方法名:属于标识符,见名知意
-
形参列表:方法可以声明0个,1个或多个形参。
-
return关键字的使用:
- 方法的使用:可以调用当前类的属性或方法;方法中不可以定义其他的方法
上述案例的代码
package com.atguigu.test;
/**
* @author:Ven
* @date:2020/7/20 - 14:14
* @function:
*/
public class StudentTest01{
public static void main(String[] args) {
//声明student类型的数组
Student01[] students = new Student01[20];
for (int i = 0; i < students.length; i++) {
//给数组元素赋值
students[i] = new Student01();
//学号1-20
students[i].number = (i+1);
//年级:1-6
students[i].state = (int) (Math.random()*(6-1+1)+1);
//成绩:0-100
students[i].score = (int) (Math.random()*(100-0+1));
}
//遍历学生数组
StudentTest01 test01 = new StudentTest01();
test01.printStuInfo(students);
System.out.println("********************************");
//问题1 打印出3年级学生的信息
test01.searchState(students, 3);
System.out.println("********************************");
//问题2 使用冒泡排序按学生成绩排序,并遍历所有学生信息
test01.sortStuInfo(students);
//遍历学生信息
test01.printStuInfo(students);
}
//遍历学生数组
public void printStuInfo(Student01[] student01s){
//遍历学生信息
for (int i = 0; i < student01s.length; i++) {
System.out.println(student01s[i].number+"\t"+student01s[i].score+"\t"+student01s[i].state);
}
}
//打印出3年级学生的信息方法
public void searchState(Student01[] student01s, int state){
for (int i = 0; i < student01s.length; i++) {
if (student01s[i].state==state){
System.out.println(student01s[i].number+"\t"+student01s[i].score);
}
}
}
//使用冒泡排序按学生成绩排序,并遍历所有学生信息
public void sortStuInfo(Student01[] student01){
for (int i = 0; i < student01.length-1; i++) {
for (int j = 0; j < student01.length - 1 - i; j++) {
if (student01[j].score>student01[j+1].score){
Student01 temp = student01[j];
student01[j] = student01[j+1];
student01[j+1] = temp;
}
}
}
}
}
class Student01{
int number;//学号
int state;//年级
int score;//成绩
}
上述案例的内存解析
编译完源代码以后,生成一个或多个字节码文件。
我们使用JVM中的类的加载器和解释器对生成的字节码文件进行解释运行。意味着,需要将字节码文件对应的类加载到内存中,涉及到内存解析。
虚拟机栈,即为平时提到的栈结构,我们将局部变量存储到栈结构中。
堆,new出来的结构(比如数组、对象)。对象的属性(非static)
- 内存解析案例:
⑤方法的重载
定义:在同一个类中,允许存在一个以上的同名方法,只要他们的参数个数或者参数类型不同即可
“两同一不同”:同一个类、同一个方法名,参数列表不同(参数个数、参数类型)
public class OverLoadTest {
public static void main(String[] args) {
OverLoadTest test = new OverLoadTest();
test.getSum(1,2);
}
//如下的4个方法构成了重载
public void getSum(int i,int j){System.out.println("1");}
public void getSum(double d1,double d2){System.out.println("2");}
public void getSum(String s ,int i){System.out.println("3");}
public void getSum(int i,String s){System.out.println("4");}
//如下的3个方法不能与上述4个方法构成重载
// public int getSum(int i,int j){return 0;}
// public void getSum(int m,int n){}
// private void getSum(int i,int j){}
}
⑤可变个数形参
-
jdk5.0新增特性,允许直接定义能和多个实参相匹配的形参。通常用在与数据库交互的时候!
-
具体使用:
-
格式:方法名(参数的类型名 …参数名)
-
当调用可变个数参数的方法时,参数个数可以是0个、1个。。。
-
可变个数形参的方法与本类方法中方法名相同,形参个数不同的方法之间构成重载
-
可变个数形参的方法与本类中方法名相同,形参类型也相同的数组,不能共存。
-
可变个数形参必须声明在末尾!并且也只能存在一个可变个数形参。
public void show (int i , String … strs)
-
public class MethodArgsTest {
public static void main(String[] args) {
MethodArgsTest test = new MethodArgsTest();
test.show(19);
test.show("jiWei");
test.show("jiWei", "sunYan", "liuZiXin");
}
public void show(int i){
System.out.println(i);
}
public void show(String s){
System.out.println(s);
}
public void show(String...s){
System.out.println(s);
for (int i = 0; i < s.length; i++) {
System.out.println(s[i]);
}
}
}
⑥方法参数的值传递机制
-
关于变量的赋值
- 如果变量是基本数据类型,此时赋值的是变量所保存的数据值
- 如果变量是引用数据类型,此时赋值的是变量所保存的数据的地址值
-
关于方法的形参的传递机制:值传递
- 形参:方法定义时,声明的小括号内的参数
- 实参:方法调用时,实际传递给形参的数据
值传递机制的内存解析
- 案例1:
- 案例2:
- 案例3(!!!!)
- 案例4(!!!!)
- 案例5(!!!!)
-
案例6(!!!)
⑦递归方法的使用(了解)
需要从内存中方法的入栈出栈进行分析!!
2 OOP特征:封装性
①思想
把该隐藏的隐藏起来,把该暴露的暴露出来。
具体表现为信息的隐藏:
- 通过将数据声明为私有的private,再提供公共的public方法:**getXxx()和setXxx()**实现对属性的操作。
class Animal {
private int legs;// 将属性legs定义为private,只能被Animal类内部访问
public void setLegs(int i) { // 在这里定义方法 eat() 和 move()
if (i != 0 && i != 2 && i != 4) {
System.out.println("Wrong number of legs!");
return; }
legs = i; }
public int getLegs() {
return legs; } }
public class Zoo {
public static void main(String args[]) {
Animal xb = new Animal();
xb.setLegs(4); // xb.setLegs(-1000);
//xb.legs = -1000; // 非法
System.out.println(xb.getLegs());
} }
②权限修饰符
3构造器
①概念
构造器或者成为构造方法
构造器又分为了无参构造器和有参构造器。
系统默认提供一个隐式的无参构造器;也可以自己显式定义多个构造器(有参或无参)
特点:
-
具有和类相同的名称
-
不声明返回值:public Person(){}
-
修饰符有:不能被static、final、synchronized、abstract、native修饰,不能有return语句返回值。
②作用
创建对象;给对象进行初始化!
比如:Order o = new Order(); Person p = new Person(“Peter”,15);
我的理解就是,就是初始化不同类型的对象,有的对象初始有属性,有的初始没有属性。
③代码
案例1:
public class Animal {
private int legs;
//无参构造器
public Animal(){
//构造器内初始化属性,也就是说初始化对象的时候,默认animal就有4个legs
legs = 4;
}
public void setLegs(int i){
legs = i;
}
public int getLegs(){
return legs;
}
}
public class AnimalTest {
public static void main(String[] args) {
Animal animal = new Animal();
System.out.println(animal.getLegs());//4
}
}
案例2:
public class PersonTest {
public static void main(String[] args) {
Person person = new Person();
System.out.println(person.getAge());//因为在构造器内初始化了age=18
person.setAge(29);//使用setAge重新设置age = 29
System.out.println(person.getAge());
System.out.println(person.getName());//string类型默认值为null
person.setName("jiwei");
System.out.println(person.getName());
}
}
public class Person {
//私有属性
private String name;
private int age;
//无参构造器,设置age默认值为18
public Person(){
age = 18;
}
public void setName(String name){
this.name = name;
}
public String getName(){
return name;
}
public void setAge(int age){
this.age = age;
}
public int getAge (){
return age;
}
}
案例3:有参构造器和无参构造器对比:
//测试类
public class TriAngleTest {
public static void main(String[] args) {
//使用无参构造器实例化一个三角形
TriAngle angle = new TriAngle();
angle.setBase(5.0);
angle.setHeight(10.0);
System.out.println(angle.findArea());
System.out.println("==============");
//使用有参构造器实例化一个三角形
TriAngle angle1 = new TriAngle(6.0, 3.0);
System.out.println(angle1.findArea());}}
//三角形类
public class TriAngle {
private double base;//底
private double height;//高
//无参构造器
public TriAngle(){}
//有参构造器
public TriAngle(double base, double height){
this.base = base;
this.height = height;}
//get、set方法
public double getBase() {
return base;}
public void setBase(double base) {
this.base = base; }
public double getHeight() {
return height;}
public void setHeight(double height) {
this.height = height; }
/**
* @Author jiwei
* @Description //TODO 计算三角形面积
* @return 三角形的面积
**/
public double findArea(){
return height*base*0.5;}}
④构造器重载
举例:
public class Person {
private String name;
private int age;
private Date birthDate;
public Person(String n, int a, Date d) {
name = n;
age = a;
birthDate = d; }
public Person(String n, int a) {
name = n;
age = a; }
public Person(String n, Date d) {
name = n;
birthDate = d; }
public Person(String n) {
name = n;
age = 30;
} }
⑤JavaBean
JavaBean指符合如下标准的Java类:
- 类是公共的
- 有一个无参的公共的构造器
- 有属性,且有对应的get、set方法
JavaBean示例:
public class JavaBean {
private String name; // 属性一般定义为private
private int age;
public JavaBean() {
}
public int getAge() {
return age; }
public void setAge(int a) {
age = a; }
public String getName() {
return name; }
public void setName(String n) {
name = n; } }
⑥UML类图
4关键字
①this关键字
它在方法内部使用,即这个方法所属对象的引用;
它在构造器内部使用,表示该构造器正在初始化的对象
this 可以调用类的属性、方法和构造器
②package关键字
package语句作为Java源文件的第一条语句,指明该文件中定义的类所在
的包。(若缺省该语句,则指定为无名包)。它的格式为:
package 顶层包名**.**子包名 ;
举例:
pack1\pack2\PackageTest.java
package pack1.pack2; //指定类PackageTest属于包pack1.pack2
public class PackageTest{
public void display(){
System.out.println("in method display()");
} }
包对应于文件系统的目录,package语句中,用 “.” 来指明包(目录)的层次;
包通常用小写单词标识。通常使用所在公司域名的倒置:com.atguigu.xxx
③import关键字
import语句告诉编译器到哪里去寻找类。
语法格式:
import 包名**.** 类名**;**