文章目录
1、Java面向对象学习的三条主线
- Java类及类的成员:属性、方法、构造器;代码块、内部类
- 面向对象的三大特征:封装性、继承性、多态性、(抽象性)
- 其它关键字:this、super、static、final、abstract、interface、package、import等
2、面向对象与面向过程
-
面向过程(pop)与面向对象(oop)
二者都是一种思想,面向对象是相当于面向过程而言的。面向过程,强调的是功能行为,以函数为最小单位,考虑怎么做。面向对象,将功能封装进对象,强调具备了功能的对象,以类/对象为最小单位,考虑谁来做
2.1 面向对象和面向过程的区别
-
面向过程:
- 把冰箱门打开
- 抬起大象,塞进冰箱
- 把冰箱门关闭
-
面向对象:
-
人{
打开(冰箱){
冰箱.打开();
}
抬起(大象){
大象.进入(冰箱);
}
关闭(冰箱){
冰箱.闭合();
}
}
-
冰箱{
打开(){}
闭合(){}
}
-
大象{
进入(冰箱){
}
}
-
2.2 面向对象的三大特征
- 封装性
- 继承性
- 多态性
3. Java语言的基本元素
3.1 类和对象
-
类(Class)和对象(Object)是面向对象的核心概念
- 类是对一类事物的描述,是抽象的、概念上的定义
- 对象是实际存在的该类事物的每个个体,因而也被称为实例(instance)
-
可以理解为:类 = 抽象概念的人
对象 = 实实在在的某个人
-
面向对象程序设计的重点是类的设计
-
类的设计,其实就是类的成员的设计
3.2 类的结构–属性和方法
-
常见类的成员有:
- 属性:对应类中的成员变量
- 行为:对应类中的成员方法
Field = 属性 = 成员变量,Method = (成员)方法 = 函数
field(域、字段)
3.3 类和对象的创建
- 创建类的对象 = 类的实例化 = 实例化类
- 调用对象的结构:属性、方法
- 调用属性:“对象.属性”
3.4 类和对象的使用(面向对象思想落地的实现)
- 创建类,设计类的成员
- 创建类的对象(类的实例化)
- 通过"对象.属性" 或 "对象.方法"调用对象的结构
3.5 体会类的多个对象的关系
//测试类
public class PersonTest{
public static void main(String[] args){
//创建Person类的对象
Person p1 = new Person();
//调用对象的结构:属性、方法
//调用属性:"对象.属性"
p1.name = "Tom";
p1.isMale = true;
System.out.println(p1.name);
//调用方法:"对象.方法"
p1.eat();
p1.sleep();
p1.talk("中文");
//***********************************
//创建类的另外一个对象
Person p2 = new Person();
System.out.println(p2.name);//null,不报错,说明属性有默认初始化值
System.out.println(p2.isMale);//false
//将p1变量保存的地址值赋给p3,导致p1和p3指向了堆空间中的同一个对象实体。
Person p3 = p1;
System.out.println(p3.name);
p3.age = 10;
System.out.println(p1.age);//10
}
}
class Person{
//属性有默认初始化值
String name;
int age = 1;
boolean isMale;
//方法
public void eat(){
System.out.println("人可以吃饭");
}
public void sleep(){
System.out.println("人可以睡觉");
}
public void talk(String language){
System.out.println("人可以说话,使用的是:" + language);
}
}
- 如果创建了一个类的对象,每个对象都独立的拥有一套类的属性(非static的),意味着,如果我们修改一个对象的属性a,则不影响另外一个对象属性a的值
3.6 对象的内存解析
- 堆(heap):存放实例对象(即我们创建的真实的结构的实体)
- 栈(stack):是指虚拟机栈,用于存储局部变量等。局部变量表存放了编译期可知长度的各种基本数据类型(boolean、byte、char、short、int、float、long、double)、对象引用类型,它不同于对象本身,是对象在堆内存的首地址。方法执行完,自动释放
- 方法区(Method Area),用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据
3.7 属性与局部变量的对比
1. 类中属性的使用
属性(成员变量) VS 局部变量
-
相同点:
- 定义变量的格式:数据类型 变量名 = 变量值
- 先声明 后使用
- 变量都有其对应的作用域
-
不同点
-
在类中声明的位置不同
属性(成员变量):直接定义在类的一对{}内
局部变量:声明在方法内、方法形参、代码块内、构造器形参、构造器内部的变量
-
关于权限修饰符的不同
(1)对于属性来讲:可以在声明属性时,指明其权限,使用权限修饰符。
常用的权限修饰符:private、public、缺省、protected,权限影响了结构被调用时可见性的大小,即有的可以被调用,有的不可以
(2)局部变量:不可以使用权限修饰符(它们的权限相当于用public代替了,方法是public,里面的局部变量就是public)
-
默认初始化值的情况:
属性:类的属性,根据其类型,都有默认初始化值
局部变量:没有默认初始化值
意味着:我们在调用局部变量之前,一定要显式赋值
特别地:形参在调用时,我们赋值即可
-
在内存中加载的位置:
属性:加载到堆空间中(非static)
局部变量:加载到栈空间
public class UserTest{ public static void main(String[] args){ } } class User{ //属性(或成员变量) String name; int age; boolean isMale; public void talk(String language){//language形参,局部变量 System.out.println("我们使用" + language); } public void eat(){ String food = "烙饼";//局部变量 System.out.println("北方人喜欢吃:" + food); } }
-
2. 方法举例与声明的格式
- 方法的声明:权限修饰符 返回值类型 方法名(形参列表){
方法体
}
-
说明:
-
关于权限修饰符:
Java规定的4中权限修饰符:private、public、缺省、protected
-
返回值类型:
有返回值 VS 没有返回值
(1)如果方法有返回值,则必须在方法声明时指定返回值类型。同时,需要使用return关键字来返回指定类型的变量和常量
(2)如果方法没有返回值,则方法声明时,使用void来表示。通常,没有返回值的方法中,就不需要使用return。但是如果使用的话,只能"return;"表示结束此方法的意思。
(3)我们定义方法是该不该有返回值?
题目要求
看经验:具体问题具体分析
(4)方法名:属于标识符,遵循标识符的规则和规范,同时,“见名知意”
(5)形参列表:方法可以声明0个,1个,或多个形参
格式:数据类型1 形参1,数据类型2 形参2,…
定义方法时,要不要定义形参?
(6)方法体:方法功能的体现
-
/*
类中方法的声明和使用
方法:描述类应该具有的功能
比如:Math类:sqrt()\random()\...
Scanner类:nextXXX()...
*/
public class CustomerTest{
}
//客户类
class Customer{
String name;
int age;
boolean isMale;
//方法
public void eat(){
System.out.println("客户吃饭");
}
public void sleep(int hour){
System.out.println("休息了" + hour + "小时");
}
public String getName(){
renturn name;
}
public String getNation(String nation){
String info = "我的国籍是:" + nation;
return info;
}
}
-
方法的分类:按照是否有形参及返回值
无返回值 有返回值 无形参 void 方法名(){} 返回值类型 方法名(){} 有形参 void 方法名(形参列表){} 返回值类型 方法名(形参列表){}
3. return关键字的使用
-
适用范围:使用在方法体中
-
作用:(1)结束方法
(2)针对于有返回值类型的方法,使用"return 数据"方法返回所要的数据。
-
注意:return后面不能声明执行语句。
4.方法使用的注意点:
-
可以调用当前类的属性或方法
特殊的:方法a中又调用了方法a:递归方法
-
方法中不可以定义方法
3.8 练习
class Person{
String name;
int age;
/**
sex: 1表明是男性
sex: 0表明是女性
*/
int sex;
public void study(){
System.out.println("studying");
}
public void showAge(){
System.out.println("年龄为:" + age);
}
public int addAge(int i){
age += i;
//返回值
return age;
}
}
public class PersonTest{
public static void main(String[] args){
//创建对象
Person p1 = new Person();
p1.name = "Tom";
p1.age = 18;
p1.sex = 1;
p1.study();
p1.showAge();
int newAge = p1.addAge(2);
System.out.println(p1.name + "的新年龄为:" + newAge);//20
Person p2 = new Person();
p2.showAge();//0
//哪个对象调用方法,属性就是谁的
}
}
4. JVM内存结构与对象内存解析
编译完源程序以后,生成一个或多个字节码文件。
我们使用JVM中的类的加载器和解释器对生成的字节码文件进行解释运行。意味着,需要将字节码文件对应的类加载到内存中,涉及到内存解析。
-
虚拟机栈:即为栈结构。我们将局部变量存储在栈结构中
-
堆,我们将new出来的结构(比如:数组、对象)加载在堆空间中。
-
补充:对象的属性(非static)加载到堆空间中
-
方法区:类的加载信息、常量池、静态域
5. 万事万物皆对象
-
在Java语言范畴中,我们都将功能、结构等封装到类中,通过类的实例化,来调用具体的功能结构
Scanner,String等
文件:File
网络资源:URL
-
涉及到Java语言与前端HTML、后端的数据库交互时,前后端的结构在Java层面交互时,都体现为类、对象
6. 对象数组的内存解析
- 引用类型的变量,只可能存储两类值:null 或 地址值(含变量的类型)
7. 匿名对象
7.1 匿名对象的使用
- 理解:我们创建的对象 ,没有显式的赋给一个变量名。即为匿名对象
- 特征:匿名对象只能调用一次
public class PhoneTest{
public static void main(String[] args){
Phone p = new Phone();
System.out.println(p);
p.sendEmail();
//匿名对象
//new Phone();就是对象没有名
//用法
new Phone().sendEmail();
new Phone().playGame();//与new Phon().sendEmain()不是同一个对象
}
}
class Phone{
double price;
public void sendEmail(){
System.out.println("发送邮件");
}
}
-
使用:
class Phone{ double price; public void sendEmail(){ System.out.println("发送邮件"); } } class PhoneFactory{ public void show(Phone phone){//方法的形参是Phone类型 phone.sendEmail(); } } public class PhoneTest{ public static void main(String[] args){ PhoneFactory factory = new PhoneFactory(); //匿名对象的使用 factory.show(new Phone()); }
8. 自定义数组的工具类
public class ArrayUtil{
//求数组的最大值
public int getMax(int[] arr){
}
//求数组的最小值
//求数组的总和
//求数组的平均值
//反转数组
}
public class ArrayTest{
public static void main(String[] args){
//调用方法
}
}
9. 构造器(或构造方法、constructor)
- 任何一个类都有构造器,且默认构造器的权限和类的权限相同
9.1 构造器的作用
-
用来创建对象
public class PersonTest{ public static void main(String[] args){ //创建类的对象:new + Person p = new Person(); p.eat(); } } class Person{ //属性 String name; int age; //构造器 public Person(){ sout("Person()...");//在new Person()的时候执行 } //方法 public void eat(){ sout("人吃饭"); } public void sleep(){ sout("人睡觉"); } }
//构造器
public class PersonTest{
Person p1 = new Person("Tom");
sout(p1.name);
}
class Person(String n){
this.name = n;
}
9.2 说明:
-
如果没有显式的定义类的构造器的话,则系统默认提供一个空参的构造器
-
定义构造器的格式:权限修饰符 类名(形参列表){}
-
构造器不等同于方法
(1)格式不一样
(2)功能不一样:
方法,有了对象之后去调方法;
构造器,来造对象的。
9.3 细节说明
-
构造器的特征
(1)它具有与类形同的名称
(2)它不声明返回值类型(与声明为void不同)
(3)不能被static、final、synchronized、abstract、native、修饰,不能有return语句返回值
-
构造器的作用
(1)创建对象
(2)给对象进行初始化
-
在一个类中可以定义多个构造器,这些构造器彼此构成重载
-
一旦我们显式的定义了类的构造器之后,系统就不再提供默认的空参构造器
-
一个类中,至少会有一个构造器
9.4 构造器练习
- 利用构造器设置所有人的age属性初始值都为18
public Person(){
age = 18;
}
-
三角形
TriAngle类
public class TriAngle{ //属性 private double base; private double height; //构造器 public TriAngle(){ } public TriAngle(double b,double h){ base = b; height = h; } //方法 public void setBase(double b){ base = b; } public double getBase(){ return base; } public void setHeight(double h){ height = h; } public double getHeight(){ return height; } }
-
测试类
public class TriAngleTest{ public static void main(String[] args){ TriAngle t1 = new TriAngle(); sout("base:" + t1.getBase() + ", height:" + t1.getHeight()); } TriAngle t2 = new TriAngle(5.1, 5.6); sout("base:" + t2.getBase() + ", height:" + t2.getHeight()); }
10. 属性赋值过程
10.1 总结:属性赋值先后过程
-
几种赋值方式:
(1)默认初始化值
(2)显式初始化
(3)构造器中初始化
(4)通过"对象.方法" 或 “对象.属性” 的方式,赋值(操作可以反复执行)
以上操作的先后顺序:(1)—> (2)—> (3)—> (4)
11. 拓展—JavaBean的使用
-
JavaBean是一种Java语言写成的可重用组件
-
所谓JavaBean,是指符合如下标准的Java类:
(1)类是公共的
(2)有一个无参的公共构造器
(3)有属性,且有对应的get、set方法
public class Customer{ private int id; private String name; public Customer(){ } public void setId(int i){ id = i; } public int getId(){ return id; } public void setName(String n){ name = n; } public String getName(){ return name; } }
-
用户可以使用JavaBean将功能、处理、值、数据库访问和其他任何可以用Java代码创造的对象进行打包,并且其他的开发者可以通过内部的JSP页面、Servlet、其他JavaBean、applet程序或者应用来使用这些对象。用户可以认为JavaBean提供了一种随时随地的复制和粘贴的功能,而不用关系任何改变。
12. 拓展:UML类图
-
(1)+表示 public 类型,- 表示 private 类型, # 表示protected类型
(2)方法的写法:
方法的类型(+、-) 方法名(参数名:参数类型): 返回值类型
13. 类的成员之四—代码块
类的成员之四:代码块(或初始化块)
-
代码块的作用:通常是用来初始化当前类、或者对象的
-
代码块如果有修饰的话,只能使用static
-
分类:
(1)静态代码块
(2)非静态代码块
-
静态代码块:
static{ sout("hello, static block"); }
(1)内部可以有输出语句
(2)随着类的加载而执行(非加载,加载只是加载到内存中,需要调用才能执行)
(3)且只执行一次
(4)作用:初始化类的一些信息
(5)在一个类中,可以定义多个静态代码块;并且按照声明的先后顺序执行
(6)静态代码块的执行要优先于非静态代码块的执行
实际使用中,没有必要造多个
(7)静态代码块内只能调用静态的属性、静态的方法,不能调用非静态的结构
-
非静态代码块
{ sout("hello, blocks") }
(1)内部可以有输出语句
(2)随着对象的创建而执行(3)且每创建一个对象就执行一次非静态代码块
(4) 作用:可以在创建对象时,对对象的属性进行初始化
(5)如果一个类中定义了多个非静态代码块按照声明的先后顺序执行
(6)非静态代码块内可以调用静态的属性、静态的方法,或非静态的属性、非静态的方法
public class BlockTest{
}
class Person{
//属性
String name;
int age;
static String desc = "我是一个人";
//构造器
public Person(){
}
public Person(String name, int age){
this.name = name;
this.age = age;
}
//代码块
{
}
//方法
public void eat(){
sout("吃饭");
}
//提供一个toString的方法
public String toString(){
return "Person [name = " + name + ", age = " + age + "]";
}
}
-
小总结:对象可以赋值的位置
- 默认初始化
- 显式初始化
- 构造器中初始化
- 有了对象以后,可以通过"对象.属性" 或 "对象.方法"的方式,进行赋值
- 在代码块中赋值
13.1 属性赋值的先后顺序
a — b/e — c — d
b 、 e先写谁 谁就先执行
14. 类的内部成员之五 — 内部类
当一个事物的内部,还有一个部分需要一个完整的结构进行描述,而这个内部的完整的结构又只为外部事物提供服务,那么整个内部的完整结构最好使用内部类—即这个类在没必要在外面定义,只是自己去用
在Java中,允许一个类的定义位于另一个类的内部,前者称为内部类,后者称为外部类。
Inner class 一般用在定义它的类或语句块之内,在外部引用它时必须给出完整的名称
Inner class 的名字不能与包含外部类类名相同
分类:成员内部类(static成员内部类和非static成员内部类)
局部内部类(不谈修饰符)、匿名内部类
14.1 使用
- Java中允许将一个类A声明在另一个类B中,则类A就是内部类,类B称为外部类
- 内部类的分类:成员内部类(静态的、非静态的) VS 局部内部类(方法内、代码块内、构造器内)
public class InnerClassTest{
}
class Person{
String name;
public void eat(){
sout("人:吃饭");
}
//静态成员内部类
static class Brain {
String name;
int year;
public void show(){
sout("...");
}
}
//非静态成员内部类
class Body {
String name;
public Body(){
}
public void action(){
sout("身体的一些行为是健康状态的预警");
Person.this.eat();//调用外部类的非静态属性。
}
}
public void method(){
//局部内部类
class AA {
}
}
{
//局部内部类
class BB {
}
}
public Person(){
//局部内部类
class CC{
}
}
}
14.2 成员内部类的特点
-
一方面:作为外部类的成员
-
另一方面,作为一个类:
(1)类内可以定义属性、方法、构造器等
(2)可以被final修饰,表示此类不能被继承。言外之意:不使用final,就可以被继承
(3)可以被abstract修饰(表示此类不能被实例化)。
14.3 如何实例化成员内部类
-
如何实例化成员内部类的对象
//创建Brain实例(静态的成员内部类): Person.Brain brain = new Person.Brain(); brain.show; //创建Body实例:(非静态的成员内部类) //要先有外部类的实例 Person p = new Person(); Person.Body body = p.new Body(); body.action();
-
如何在成员内部类中区分调用外部类的结构
class Body { String name; public Body(){ } public void action(){ sout("身体的一些行为是健康状态的预警"); Person.this.eat();//调用外部类的非静态属性。 } public void display(String name){ sout(name);//方法形参name sout(this.name);//Body内部类的name属性 sout(Person.this.name);//Person外部类的name属性 } }
-
开发中局部内部类的使用
public class InnerClassTest1{
//开发中很少见
public void method(){
//局部内部类
class AA{
}
}
//返回一个实现了Comparable接口的类的对象
public Comparable getComparable(){
//创建一个实现了Comparable接口的类:局部内部类
//方式一:
class Mycomparable implements Comparable{
@Override
public int compareTo(Object o){
return 0;
}
}
return new MyComparable();
//方式二:
return new Comparable(){
@Override
public int compareTo(Object o){
return 0;
}
};
}
}
14.4 局部内部类使用的一个注意点
在局部内部类的方法中(比如:show),如果调用局部内部类所声明的方法(比如:method)中的局部变量的话,要求此局部变量声明为final的
public class InnerClassTest {
public void method(){
//局部变量:在JDK7及以前的版本中,需要明确的加上final--->final int num = 10;
int num = 10;
class AA {
public void show(){
//报错:Local variable num defined in an enclosing scope must be final or effectively final
//num = 20;
sout(num);
}
}
}
}
外部类生成一个字节码文件,内部类也生成一个独立的字节码文件,method方法本身存在于外部类的结构里,num变量正常来讲出了方法就失效了,要想在另一个字节码文件中能用,加上final可以理解为:在另一个字节码文件中可以用,相当于给它传了一个副本(意味着能用不能改)。