面向对象【Java语言的核心机制】
-
面向对象三大特性:封装、继承、多态。【所有面向对象的语言都有】
-
面向对象和面向过程区别:
*面向对象:主要关注对象【独立体】能完成那些功能。【例如独立显卡开发思路】
优点:耦合度低,扩展力强。更容易解决现实世界中更复杂的业务逻辑,组件复用性强。
缺点:前期投入成本高,需要进行独立体的抽取,大量系统分析与设计。
*面向过程:主要关注实现具体过程,因果关系【例如集成显卡的开发思路】
优点:对业务逻辑比较简单的程序,可以到达快速开发,前期成本较低。
缺点:采用面向过程的方式开发很难解决非常复杂的业务逻辑,另外面向过程的方式导致软件元素之间的“耦合度”非常高,只要其中一环出现问题,整个系统受影响,导致最终的软件扩展力差。且由于没有独立体概念,无法到达组件复用。
*C语言是纯面向过程的,C++半面向对象,Java纯面向对象。
- 面向对象开发软件生命周期【整个生命周期贯穿使用OO面向对象】
-面向对象分析:OOA
-面向对象设计:OOD
-面向对象编程:OOP
- 类和对象
-类:在现实世界不存在的,是抽象出来的概念。类代表了一个事物。在现实世界当中,对象A和对象B之间具有共同特征,进行抽象出来一个模板,这个模板称为类。
-对象:对象是实际存在的个体,现实世界当中实际存在。
*类到对象的过程称为“实例化”,对象又被称为实例【instance】,对象到类的过程称为“抽象”。
*一个类主要描述:状态+动作。
状态——>类的属性。描述状态信息。
动作——>类的方法。描述动作信息。
1.面向对象特性:封装
封装优点:
*封装之后,对于封装的事物来说,看不到事物复杂的一面,只能看到简单的一面。复杂性封装,对外提供简单的操作入口。
如:照相机。照相机实现原理较为复杂,但是操作照相机非常便捷。
*封装之后才会形成真正的“对象”,真正的“独立体”。
*封装意味着程序可以重复使用,且这个事物适应性较强,在任何场合都可以使用。
*封装之后,对于事物本身,提高了安全性。【安全级别高】
1.封装步骤:
(1)对所有属性私有化,使用private关键字进行修饰,private表示私有的,修饰的所有数据只能在本类中访问。
(2)对外提供简单的操作接口,也就是如果外部程序想要访问对象中的属性,必须通过这些简单入口进行访问:
-对外提供两个公开的方法,分别是set方法和get方法
-想修改属性调用set方法
-想读取属性调用get方法
(3)set方法命名规范:public void set+属性名首字母大写(形参){}
//例如
public void setAge(int a){
age = a;
}
(4)get方法命名规范:public ElemType get+属性名首字母大写(){} 【ElemType 表示数据类型】
//例如
public int getAge(){
return age;
}
注:
—setter 和 getter方法没有static 关键字
—有static关键字修饰的方法调用:类名.方法名(实参);
—没有static关键字的方法调用:引用.方法名(实参);
2.set和get
在操作入口只能通过set和get方法进行访问后,在set方法和get方法执行过程中可以进行安全过滤。
//例如:要在修改数据中判断输入的参数是否在一个区间范围
public void setAge(int a){
if(a<0 || a>150){
System.out.println("输入的数据有误");
return;//结束调用
}
//程序能执行到这里,说明a合法
age = a;
}
例子:
//封装一个用户类
public class User{
//属性私有化
private int age;
//一个错误的例子,形参名字设置跟属性一样,会报错
// public void setAge(int age){
// age = age; //java有就近原则,这里的age是局部变量的age。
// }
//setter方法
public void setAge(int a){
//编写业务逻辑代码进行安全控制
// age = a;
if(a < 0 || a>150){
System.out.println("您提供的年龄不合法");
return;
}
//程序若执行到这,说明a合法,进行赋值运算。
age = a;
}
//getter方法
public int getAge(){
return age;
}
}
public class UserTest{
public static void main(String[] args){
//创建User对象
User user = new User();
//System.out.println(user.age);
//编译报错,age已经属性私有化了,外部程序不能直接访问。
//修改数据
user.setAge(-100); //设置了安全过滤,数字小于0未修改age值
//读取
System.out.println(user.getAge()); //0
//修改数据
user.setAge(23);
//读取
System.out.println(user.getAge()); //23
}
}
2.构造方法
关于java类中的构造方法:
1.构造方法又被称为构造函数/构造器/Constructor
2.构造方法语法结构:
[修饰符列表] 构造方法名(形式参数列表){ 构造方法体; }
3.回顾普通方法的语法结构:
[修饰符列表] 返回值类型(形式参数列表){ 方法体; }
4.对于构造方法来说:"返回值类型"不需要指定,并且也不能写void。只要写上void,方法就成为普通方法。
5.对于构造方法来说:构造方法的方法名必须和类名保持一致。
6.构造方法的作用:构造方法存在的意义是,通过构造方法的调用可以创建对象。同时初始化实例变量的内存空间。
7.构造方法调用:
-普通方法调用:方法修饰符中有static的时候:” 类名.方法(实参列表) “ 、方法修饰符列表中没有static的时候:“ 引用.方法名(实参列表) ”
-构造方法调用:new 构造方法名(实参列表)
8.每个构造方法执行结束之后都有返回值,但"return 值;" 这样的语句不需要写。构造方法结束的时候java程序自动返回值。
并且返回值类型是构造方法所在类的类型。 由于构造方法返回值类型就是类本身,所以返回值类型不需要编写。
9.当一个类中没有定义任何构造方法的话,系统默认给该类提供一个无参数构造方法,这个构造方法被称缺省构造器。
10.当一个类显示的将构造方法定义出来了,那么系统则不再默认为这个类提供缺省构造器。开发中手动为当前类提供无参数构造方法。因为无参数构造方法比较常用。
11.构造方法支持重载机制。在一个类当中编写多个构造方法,这多个构造方法显然已经构成重载机制。
例子:
public class User(){
public User(){
System.out.println("这是一个无参构造器");
}
public User(int i){
System.out.println("这是一个带int类型的构造器");
}
public User(String name){
System.out.println("这是一个带String类型的构造器");
}
public User(int i ,String name){
System.out.println("这是一个带int类型和String类型的构造器");
}
}
public class Test(){
public static void main(String[] args){
User u1 = new User(); //这是一个无参构造器
User u1 = new User(1); //这是一个带int类型的构造器
User u1 = new User("name"); //这是一个带String类型的构造器
User u1 = new User(1,"name"); //这是一个带int类型和String类型的构造器
}
}
3.参数传递
- java在方法调用涉及到参数传递时传递方式:值传递
例子:
public class Test{
public static void main(String[] args){
int i = 10;
//这里的调用属于传值调用
add(i); //等同于add(10);
System.out.println("main——> " + i); //main——>10
}
public static void add(int i ){
i++;
System.out.println("add——> " + i); //add——>11
}
}
java语言放中,方法调用的时候涉及到参数传递的问题:参数传递实际上只传递了变量中保存的具体值。
public class Test02 {
public static void main(String[] args){
User u = new User(20);
//这里u的值是一个引用。而这里的u的值是一个java对象的内存地址。
add(u); //等同于add(0x1234);
System.out.println("main——>" + u.age); //main——>21
}
public static void add(User u){
u.age++;//所以这里u里边的属性更改的时候,会被更改。而u本身存储的地址不会被更改。
System.out.println("add ——>" + u.age); //add——>21
}
}
class User{
int age;
public User(int i){
age = i;
}
}
综上:方法调用的时候,涉及到参数传递的问题,传递的时候,java只遵循一种语法机制。就是将变量中保存的“值”传递过去,只不过有时候这个值可能是一个字面值(基本数据类型),有的时候这个另一个java对象中内存地址(引用数据类型)。
4.this关键字
- 关于Java语言中this关键字:
-
this关键字,翻译为:这个
-
this是一个引用,是一个变量。this变量中保存了内存地址指向自身,this存储在JVM堆内存java对象内部。
-
创建的每个对象,都有this。
-
this可以出现在“实例方法"当中,指向代表当前正在执行的动作对象。
-
this在多数情况下可以省略不写。
-
this不能使用在带有static的方法当中。
【例】
public class Customer{
String name; //这里定义了一个实例变量
//定义一个不带static的方法
public void shopping(){
//this关键字可以省略
System.out.println(name + "在购物");//编译通过
//完整写法
//System.out.println(this.name + "在购物");
}
//定义一个带static的方法
public static void doSome(){
//带有static方法是通过类名访问,也就是说这里没有对象
//编译报错:System.out.println(name + "在购物");
//name在Customer对象中是一个实例变量,而在doSome中未创建对象时候,直接使用name是使用当前对象的name则访问不到name。
//需要创建对象后才能使用
Customer c = new Customer();
System.out.println(c.name);
}
}
【例】
public class ThisTest{
int i = 10;
//带有static的方法,JVM负责调用main方法。
//调用方式:ThisTest.main(String[]);
public static void main(String[] args){
//System.out.println(i); 编译错误
//System.out.println(this.i); 编译错误
//若想访问i
ThisTest tt = new ThisTest();
System.out.println(tt.i); // 10
}
}
【例】
public class ThisTest{
public static void main(String[] args){
ThisTest.doSome();
doSome();
//doOther(); //编译错误
//this.doOther(); //编译错误
//创建对象
ThisTest tt = new ThisTest();
//调用对象的实例变量
tt.run();
}
public static void doSome(){
System.out.println("do some");
}
//实例变量
public void doOther(){
System.out.println("do other");
}
//实例变量
public void run(){
System.out.println("run");
//在实例变量中,执行到实例变量时一定存在对象。也就是这里一定有this。
//所以在这里调用当前对象的doOther()方法。
doOther();
//完整写法:this.doOther();
}
}
-
结论:带有static的方法中,不能直接访问实例变量和实例方法。
①因为实例变量和实例方法都需要对象存在
②static的方法中是没有this,也就是当前对象不存在
③所以无法访问当前对象的实例变量和实例方法。
【例】
public class ThisTest{
//实例变量
String name;
//实例方法
public void doSome(){
}
public static void main(String[] args){
//System.out.println(name); 无法访问实例变量
//doSome(); 无法调用实例方法
ThisTest tt = new ThisTest();
System.out.println(tt.name); //通过对象访问实例变量
tt.doSome(); //通过对象访问实例方法
}
}
- 用来区分局部变量和实例变量时," this. "不能省略
【例】
//语法格式: this.
public class User{
private int id; //实例变量
public void setId(int id){
//this.id指的是实例变量
//id指的是局部变量
this.id = id;
}
}
- this可以使用在构造方法当中,通过当前构造方法调用其他构造方法。
【例】
//语法格式 this(实参)
public class Date {
private int year;
private int month;
private int day;
//需求:调用无参构造方法默认为“2001-01-01”
public Date(){
//原设置
//this.year = 2001;
//this.month = 01;
//this.day = 01;
//如果用new构造方法,会导致引用时候构造了两个对象。
//可以使用this直接调用其他构造方法,且不会创建新的对象。
//且this()只能出现在第一行
this(2001,01,01);
}
public Date(int year,int month,int day){
this.year = year;
this.month = month;
this.day = day;
}
}
5.练习
public class test06 {
//带有static的方法
public static void method1(){
//访问doSome
//使用完整方式调用
test06.doSome();
//使用省略方式调用
doSome();
//访问doOther
//使用完整方式调用
test06 t = new test06();
t.doOther();
//访问i
//使用完整方式调用
System.out.println(t.i);
}
//没有static的方法
public void method2(){
//访问doSome
//使用完整方式调用
test06.doSome();
//使用省略方式调用
doSome(); //this.doSome();
//访问doOther
//使用完整方式调用
this.doOther();
//省略方式
doOther();
//访问i
//使用完整方式调用
System.out.println(this.i);
//省略方式
System.out.println(i);
}
//主方法
public static void main(String[] args) {
//要求:调用method1
//使用完整方式调用
test06.method1();
//使用省略方式调用
method1();
//要求:调用method2
//使用完整方式调用
test06 t = new test06();
t.method2();
}
//没有static的变量
int i = 10;
//带有static的方法
public static void doSome(){
System.out.println("do some!");
}
//没有static的方法
public void doOther(){
System.out.println("doOther");
}
}
6.static关键字
- static(静态的)修饰做为静态资源使用,可修饰方法也可修饰变量。
- 当所有对象的某个属性都是一样时,建议定义为静态变量,节省内存开销。
- 静态变量在类加载中的时候初始化,访问的时候不需要创建对象。直接“ 类名. ”访问
//创建一个中国人类
public class Chinese{
//身份证号
String id;
//姓名
String name;
//由于中国人每个人的国籍都是一样的,所以使用静态变量定义
//国籍
static String country = "中国";
public Chinese(){
}
public Chinese(String id,String name){
this.id = id;
this.name = name;
}
}
public class ChineseTest{
public static void main(String[] args){
Chinese ice = new Chinese("1","冰");
System.out.println(ice.id + "," + ice.name); //1,冰
Chinese coffice = new Chinese("2","咖啡");
System.out.println(coffice.id + "," + coffice.name);//2,咖啡
//静态变量在类加载中的时候初始化,访问的时候不需要创建对象。直接“ 类名. ”访问
System.out.println(Chinese.country); //中国
}
}
- 用static关键字定义“静态代码块”,静态代码块在类加载时执行一次(在main方法前执行)。例如类加载的时刻执行代码完成日子记录。
- 静态代码块通常用来完成预备工作,先完成数据的准备工具,例如:初始化连接池,解析XML配置文件等。
public class test02 {
static {
System.out.println("类加载-->1");
}
static{
System.out.println("类加载-->2");
}
public static void main(String[] args) {
System.out.println("main方法");
}
}
/*输出结果:
类加载-->1
类加载——>2
main方法
*/
- 静态方法中无法直接访问实例变量和实例方法。
7.继承
-
面向对象三大特征:封装、继承、多态
-
继承基本的作用:代码复用,有了继承之后才有了方法的覆盖和多态机制。
-
继承语法:[修饰符列表] class 类名 extends 父类名{ 类体 = 属性 + 方法 }
-
Java当中继承只能支持单继承,一个类只能继承一个类,但是可以间接继承。C++中支持多继承。
-
可继承:私有的不支持继承、构造方法不支持继承、其他数据都可以被继承。
-
如果Java语音中一个类没有继承任何类,该类继承JAVASE中java.lang.Object类。
【例子】
//账户类
public class Account{
private String actno;
private double balance;
public Account(String actno, double balance) {
this.actno = actno;
this.balance = balance;
}
public Account() {
}
public String getActno() {
return actno;
}
public double getBalance() {
return balance;
}
public void setActno(String actno) {
this.actno = actno;
}
public void setBalance(double balance) {
this.balance = balance;
}
}
//继承账户类的信用账户
public class CreditAccount extends Account{
private double credit;
public CreditAccount(){
super();
}
public double getCredit() {
return credit;
}
public void setCredit(double credit) {
this.credit = credit;
}
}
//测试类
public class ExtendsTest {
public static void main(String[] args) {
CreditAccount act = new CreditAccount();
act.setActno("act-001");
act.setBalance(-1000.0);
act.setCredit(0.99);
System.out.println(act.getActno() + "," + act.getBalance()+","+act.getCredit());
//act-001,-1000.0,0.99
}
}
8.方法覆盖
-
方法覆盖:又称方法重写(override)
-
方法覆盖:当父类中方法无法满足当前子类业务需求,子类有必要将继承的方法进行重新编写。
-
方法覆盖发生在具有继承关系的父子类之间,返回值类型相同、方法名相同、形参列表相同。
public class Animal{
public void move(){ System.out.println("动物在移动"); }
}
public class Bird extends Animal{
public void move(){ System.out.println("鸟在飞翔"); }
}
public class Cat extends Animal{
public void move(){ System.out.println("猫在走猫步"); }
}
public class OverrideTest {
public static void main(String[] args) {
Animal a = new Animal();
a.move(); //动物在移动
Cat c = new Cat();
c.move();//猫在走猫步
Bird b = new Bird();
b.move();//鸟在飞翔
}
}
- 私有方法不能继承,所以不能覆盖;构造方法不能继承,所以不能覆盖。
9.多态
-
多态概念
向上转型:子类型→父类型(自动类型转换)
向下转型:父类型→子类型(强制类型转换)
向上转型和向下转型都需要有继承关系。
//动物类
public class Animal{
public void move(){ System.out.println("动物在移动"); }
}
//猫类继承动物类
public class Cat extends Animal{
//重写父类继承过来的方法
public void move(){ System.out.println("猫在走猫步"); }
//子类对象特有,不是继承父类的方法
public void catchMouse(){ System.out.println("猫抓老鼠"); }
}
public class Test{
public static void main(String[] args){
//父类型引用指向子类型对象,这里作为向上转型
Animal c1 = new Cat();
//这里把Cat类型强制转换为Animal,然后才调用move方法。
//且这里的c1是Animal类型,所以引用不了catchMouse()方法
c1.move(); //猫在走猫步
}
}
-
以上如果需要调用catchMouse方法,需要将Animal(父类)转化为Cat(子类)。即向下转型。
当调用的方法是子类型特有的,在父类型不在,必须进行向下转型。
public class Test{
public static void main(String[] args){
//父类型引用指向子类型对象,这里作为向上转型
Animal c1 = new Cat();
//这里把Cat类型强制转换为Animal,然后才调用move方法。
//且这里的c1是Animal类型,所以引用不了catchMouse()方法
c1.move();
//需求:当c1需要调用Cat中特有的方法时,进行强制类型转换(向下转换)
Cat c2 = (Cat) c1;
c2.catchMouse(); //猫抓老鼠
}
}
- 当一个子类型被向上转型为父类,父类又进行向下转型为其他子类型。会编译报错。
//动物类
public class Animal {
public void move(){ System.out.println("动物在移动"); }
}
//鸟类继承动物类
public class Bird extends Animal{
//重写从父类中继承的方法
public void move(){ System.out.println("鸟在飞翔"); }
}
//猫类继承动物类
public class Cat extends Animal{
//重写从父类中继承过来的方法
public void move(){ System.out.println("猫在走猫步"); }
}
public class Test{
public static void main(String[] args){
Animal a3 = new Bird();
Cat c3 = (Cat) a3;
//编译异常:Exception in thread "main" java.lang.ClassCastException:
//也就是类型转换异常
}
}
-
java.lang.ClassCastException :这种为类型转换异常,做强制类型转换时会发生。
-
使用“ instanceof ”运算符可以避免ClassCastException异常。
语法格式: 引用 instanceof 数据类型名。
这个运算符执行结果是布尔类型。
c1 instanceof Animal
//运行结果true 表示 c1是一个Animal类型
//运行结果false 表示 c1不是一个Animal类型
【instanceof例子】
//动物类
public class Animal {
public void move(){ System.out.println("动物在移动"); }
}
//鸟类继承动物类
public class Bird extends Animal{
//鸟类特有的方法
public void fly(){ System.out.println("鸟飞啦"); }
}
//猫类继承动物类
public class Cat extends Animal{
//猫类特有的方法
public void catchMouse(){ System.out.println("猫抓老鼠"); }
}
public class Test{
public static void main(String[] args){
Animal a = new Bird();
if(a instanceof Cat){ //如果a是Cat类型
Cat c = (Cat)a;
c.catchMouse();
}else if(a instanceof Bird){ //如果a是鸟类型
Bird b = (Bird)a;
b.fly();
}
}
}
//最终运行结果:鸟飞啦
10.final关键字
- final修饰的类无法被继承
public final class A{ }
//public class B extends A{ }
//这里A类被final修饰,所以B无法继承A。会报错
- final修饰的方法无法被覆盖
public class C{
public final void m1(){ }
}
public class D extends C{
//public void m1(){ }
//这里m1方法给final修饰,所以无法重写,会报错
}
- final修饰变量,一旦赋值后不可重新被赋值【二次赋值】
public class A{
public static void main(String[] args){
//可先定义再赋值
final int a;
a=100;
//可定义直接赋值
final int b = 100;
//a=20;
//b=2;
//由于a和b被final修饰,所以无法重新赋值
}
}
- final修饰实例变量,必须手动赋值,不可采用系统默认值【因为实例变量有默认值,final一旦修饰变量则无法赋值,所以定义实例变量时必须赋值】
public Class A{
//final int a;
//因为实例变量有默认值,final一旦修饰变量则无法赋值,所以定义实例变量时必须赋值
final int a = 10;
final int b;
public FinalTest(){
//这里在构造方法中赋值,是可行的
this.b = 200;
}
public static void main(String[] args){
}
}
- final修饰的引用无法一旦指向某个对象之后,不能再指向其他对象【被指向的对象无法被垃圾回收期回收】
public class User{
int id;
public User(int id){
this.id=id;
}
}
public class FinalTest{
public static void main(String[] args){
//创建一个新对象
User u = new User(30);
//创建另一个新对象用u引用
//到此,上边的User(30)对象已成垃圾数据,等待垃圾回收器回收
u=new User(50);
//用final修饰用户对象
final User user = new User(35);
//final修饰引用。被指向的对象无法被垃圾回收期回收
//user= new User(40); 引用不能重新被赋值,出错
user.id=50; //使用的是引用里边的变量,内部的内存是可以修改的。
}
}
- final修饰的实例变量,这种变量一般和static配合使用,称为“常量“。
//常量的语法格式定义:public static final 类型 常量名 = 值
//规范:常量名字全部大写,每个单词之间使用下划线连接
//中国人类
class Chineses{
public static String GUO_JI = "中国";
}
public class FinalTest{
public static void main(String[] args){
System.out.println(Chinese.GUO_JI);
}
}
11.package,import
-
package(包),java引入package机制将不同功能的类分别放到不同软件当中,方便管理与维护。
-
定义package:
-在java源程序的第一行上编写package语句。
-只能编写一个语句
-语法:package 包名
-
包名的命名规范:公司域名(倒序) + 项目名 + 模块名 + 功能名;
-需要全部小写,需要遵守标识符命名规则。
-一个包对应一个目录。
package com.thetest.study.javasestudy;
public class Test{
public static void main(String[] args){
System.out.println("Test01 是一个方法执行");
}
}
//使用package机制后,类名不是Test,而是:com.thetest.study.javasestudy.Test
-
import语句用来导入其他类,同一个包下的类不需要导入,不在同一个包下需要手动导入
-
import语法格式:
-import 类名;
-import 包名.*;
-
import语句编写在package语句下面,class语句上面。
12.访问控制权限
-
访问控制权限修饰符:用来控制元素的访问范围,如public、protected、private
—public:表示公开的,任何位置都可访问
—private:表示私有的,只能在本类中访问
—protected:表示受保护的,**同包下、子类(包括其他包)**中可以访问
—缺省的(没有上面关键字的):只能在同包下访问
//在一个包中
package com.thetest.study.javasestudy;
public class User{
public static void main(String[] args){
//受保护的
protected int i = 10;
//缺省的
int j = 20;
}
}
//在另一个包下
package com.thetest.study.javasestudy2;
import com.thetest.study.javasestudy.User;
public class Test exnteds User{
public void m(){
System.out.println(i);//可以访问
//System.out.println(j);不可访问
}
}
- 普通类只能使用public、缺省进行修饰【内部类(在类中定义的类)除外】
13.super关键字
- super()只能出现在构造方法第一行,通过当前的构造方法调用“父类”中的其他构造方法,目的是代码复用。
public class superTest {
public static void main(String[] args) {
new B();
}
}
class A{
//一个类如果提供了一个构造方法,那么系统不会默认提供无参构造方法
public A(int i){
}
}
class B extends A{
public B(){
//由于A类需要有一个int参数,所以继承父类需要super(int);
//子类如果没有写super(),会默认在第一行有。
//这里默认会有一个super();所以这里会发生错误。所以要填一个super(int);
super(100);
//也就是super(100);不写,会默认有super();会导致调用一个无参构造方法,而A类只有一个有参构造方法,所以会报错。
System.out.println("B类的无参构造方法");
}
}
-
this()和super()不能共存。都是只能存在构造方法第一行。
-
“ super. ”在大部分情况下可以省略,当父类和子类有同名属性,或者同样方法,想要在子类中访问父类时“ super. ”不能省略。
语法格式:-super.属性名 【访问父类的属性】
-super.方法名(实参) 【访问父类的方法】
-super(实参) 【调用父类构造方法】
——本章节为个人学习笔记。学习视频为动力节点Java零基础教程视频:动力节点—JAVA零基础教程视频