基本概念
java类及类的成员:属性,方法,构造器;代码块,内部类 面向对象的三大特征:封装性,继承性,多态性(抽象性)
面向过程(POP)和面向对象(OOP)
面向过程:强调的是功能行为,以函数为最小单位,考虑怎做 面向对象:将功能封装进对象,强调具备了功能的对象。以类/对象为最小单位,考虑谁来做
类(Class)和对象(Object)
类是对一切事物的描述,是抽象的,概念上的定义 对象是实际存在的该类事物的每个个体,因而也称为实例 万物皆对象
可变参数
class Person{
//可变个数的形参,0到多个,一个形参匹配多个实参,在类型和形参名之间打三个点
//不能和参数为String[]的形参,重载方法,编译器认为 String ... 和 String[]一样
public void show(String ... strs){
//strs会被当成数组
for(int i=0;i<strs.length;i++){
System.out.println(strs[i]);
}
}
}
Person p1 = new Person();
p1.show("hahahah","shhsdafj","dsfs");
方法参数的传递机制
java只有值传递。
形参是基本数据类型,将实参基本数据类型变量的
数据值
传递给形参,相当于复制一份数据给形参形参是引用数据类型,将实参引用数据类型变量的
地址值
传递给形参
这个地址值,指向原来的数据,在方法里面修改数据,也是只修改原来的数据
封装
程序设计追求 高内聚,低耦合
高内聚:类的内部数据操作细节自己完成,不允许外部干涉 低耦合:仅对外暴露少量的方法用于使用
封装就需要隐藏对象内部的复杂性,只对外公开简单的接口。便于外界调用,从而提高系统的可维护性,可扩展性
我们将类的属性私有化(private),同时,提供公共的方式来get和set
class Animal{
//属性设为私有的
private String name;
private int age;
private int legs;
//通过公共的方法去设置和获取值
public int getAge(){
return age;
}
public void setAge(int a){
if(a<0||a>130)
age=0;
else
age=a;
}
}
访问权限修饰符
由小到大排列:
修饰符 | 内部类 | 同一个包 | 不同的子类 | 同一个工程 |
---|---|---|---|---|
private | - [x] |
| | | |
| (缺省)就是不写 |
- [x]
|
- [x]
| | |
| protected |
- [x]
|
- [x]
|
- [x]
| |
| public |
- [x]
|
- [x]
|
- [x]
|
- [x]
|
可以用来修饰类的内部结构:属性,方法,构造器,内部类
修饰类只能用 :缺省,public
构造器
如果没有显式的定义类的构造器,则系统默认提供一个空参的构造器
Person p1 = new Person();//这个就是new + 构造器
一旦显式的定义了类的构造器,系统就不提供默认的构造器了,也就是说,要自定义构造器。
class Animal{
//属性设为私有的
private String name;
private int age;
private int legs;
//构造器格式:权限修饰符 类名(参数){}
Animal(){
System.out.println("这是空的构造器");
}
//带参数的构造器
Animal(String a){
name=a;//实例化对象的同时,给属性赋值
}
}
this
this表示当前对象,可调用类的属性,方法和构造器
在方法内部使用,this指向的是这个方法所属对象的引用 在构造器内部使用,表示该构造器正在初始化的对象,就是这个类本身
class Animal{
//属性设为私有的
private String name;
private int age;
private int legs;
//构造器格式:权限修饰符 类名(参数){}
Animal(){
System.out.println("这是空的构造器");
}
//带参数的构造器
Animal(String a){
this();//在构造器里面可以这样调用构造器,因为在构造器里面this表示这个类本身
//this(参数) 必须写在当前构造器的首行,而且一个构造器最多只能用一次this(参数)
name=a;//实例化对象的同时,给属性赋值
}
//如果设置属性的形参和属性变量一样,就要用this指向当前对象
public void setAge(int age){
if(a<0)
this.age=0;
else if(a>130)
this.age=130;
else
this.age=age;
}
public int getAge(){
return age;
}
}
package和import
package
为了更好的实现项目的管理,提出了包的概念。在一个项目中创建多个不同功能的包,写的类就放在不同的包下面
使用package声明,声明类或者接口所属的包,声明在源文件首行
package com.test1;
//声明这个源文件在哪个包下面,要自己创建包,跟创建文件夹一样 com.test1就是包名
//包命名要见名知意
//同一个包下不能命名同名的接口或者类
import
//在源文件中显示的使用import导入指定的包下的类,接口
//写在package声明和class声明之间
import java.util.Scanner;
//用*号表示导入全部,如果是子包下的类,还是要写子包名,*只能导入当前的包的全部类和接口,不包括子包
import java.io.*;
//如果使用的类或者接口是java.lang包下定义的,则可以省略import导入
//如果使用的类或接口是本包下定义的,也可以不用import
//如果要用的类两个包里面都有重名的,只有import一个包,另一个写全限定类名
继承
减少代码冗余,提高复用性。
子类继承父类,子类就拥有了父类的全部属性和方法,便于功能扩展
继承是多态的前提
//父类,超类,基类,superclass
//如果没有显示的声明一个类的父类,则此类继承与java.lang.Object类,Object是所有类的大爹
class Animal{
//属性设为私有的
protected String name;
protected int age;
//父类属性方法等设置为private,子类还是能继承,只是由于权限不能直接调用
protected void eat(){
System.out.println("动物本能的吃了东西");
};
protected void sleep(){
System.out.println("动物本能睡觉");
};
}
//子类,派生类,subclass
public class Dog extends Animal{
//子类继承了父类,还可以单独有属于子类的字段与方法,子类进行拓展
//子类同时还有父类的属性和方法
String color;
public void running(){
System.out.println("狗跑起来了");
}
}
public class test1 {
public static void main(String[] args) {
//Animal a1 = new Animal();
Dog d1 =new Dog();
d1.age=2;
d1.runing();
d1.eat();
System.out.println(d1.age);
}
}
重写
注意:重写(override)和重载(overload)不一样
重写:子类继承父类以后,可以对父类中同名同参数的方法,进行覆盖操作 重载:同名,不同参的方法复写,参数可以个数不同,按顺序类型不同。调用时根据传入的参数决定调用哪个方法
//子类,派生类,subclass
public class Dog extends Animal{
String color;
public void running(){
System.out.println("狗跑起来了");
}
//重写父类的方法,同名,同参数,就相当于覆盖了父类的方法,不需要加特殊修饰符
//重写不能降低权限修饰符,最差也要相同
//父类返回类型是void,子类重写也只能是void。父类返回类型是A类型,重写后返回类型可以是A类的子类
//父类返回类型是基本数据类型,子类重写的返回类型只能是相同的数据类型
//子类重写的方法抛出的异常类型,不大于父类抛出的异常
public void eat(){
System.out.println("狗吃了东西");
}
}
super
显示的调用父类的属性方法,一般情况都省略。
通常情况,子类重写了父类的方法,可以用super使用父类被重写的方法,super可以用来调用属性,方法,构造器
//子类,派生类,subclass
public class Dog extends Animal{
String color;
String name;//子类和父类声明一样的属性,属性不会被覆盖
public Dog(){
}
public Dog(String name){
//this()和super()在构造器中只能二选一,没有显式声明,默认调用super()
super();//调用父类的没有参数的构造器
this.name=name;
}
public void running(){
System.out.println("狗跑起来了");
}
//重写父类的方法,同名,同参数,就相当于覆盖了父类的方法,不需要加特殊修饰符
public void eat(){
System.out.println("狗吃了东西");
}
public void show(){
System.out.println(this.name);//子类的名字
System.out.println(super.name);//父类的名字
super.eat();//父类的方法
}
}
多态
一个事物的多种形态。
//正常情况下,我们需要一个动物对象,会new一个动物类的对象
Animal a1 = new Animal();
//我们需要一个动物,猫也是动物
Animal a2 =new Cat();
//狗也是动物
Animal a3 =new Dog();
//所以动物有多种形态,这就是对象的多态性,猫狗都是动物的子类
//父类的引用指向子类的对象
多态的使用
- 类的继承关系
- 方法的重写
//多态的使用
a3.eat();//用对象的子父类同名方法的时候,会发现是执行的子类重写的方法。这就是虚拟方法调用
//Dog类中延展了一个running方法,但是Animal类中没有定义,这时候就不能调用running方法
//能调用的只能是左边类里面定义过得属性方法。编译看父类中定义的方法,运行时看子类重写的方法
//对象的多态性只适用于方法,不适用于属性(编译和运行都看左边)
多态不是编译时行为,是运行时行为,因为编译时根本看不出哪个子类,只有运行了才知道
为什么要用多态
//比如这个方法,参数是Animal类型的,我可以传入Cat类对象,也可以传入Dog类对象,什么鸡鸭鱼牛羊猪都可以
//但是如果没有多态,那这么多动物,如果都要使用这个方法那怎么办,要使用狗类,定义一个方法,参数类型Dog
//要使用猫类,定义一个方法参数是Cat类型,无穷无尽
public static void K(Animal animal){
animal.eat();
}
再举个例子,市面上数据库有很多,mysql,sqlserver,oracle等,这是用java操作这些数据库,步骤都差不多,可以说一样的操作,但是由于是不同的数据库,连接肯定不同。
想到操作都差不多,干脆今定义一个父类,父类定义好所有的操作方法,每个数据库都定义一个继承父类的子类。当你需要用什么数据库的时候,就传入数据库的子类对象赋值给父类对象。
通过一个父类对象就可以操作所有的数据库,而不是想用mysql的时候操作mysqldb类,想用oracle的时候,操作oracledb类,这么多数据库,没有多态,找用什么类都要找半天。
向下转型
有了对象的多态性以后,内存中实际上是加载了子类特有的属性和方法的,但是由于声明的变量为父类类型,导致编译时,只能调用父类中声明的属性和方法。子类特有的属性和方法不能调用。
如何才能调用子类特有的属性和方法
//判断对象a3是否是Dog类的实例,如果是返回true,如果不是返回false
//Dog换成Animal也能true
if(a3 instanceof Dog){
Dog d2= (Dog)a3;
d2.running();
System.out.println("转换成功");
}
代码块
在类中用花括号括起来的代码块,可以用static修饰。
类里面的属性,可以声明时就赋值,但是如果声明时不赋值,不可以在类里面对属性进行之后的赋值操作。
如果有一个声明东西需要重复创建操作,创建步骤还听过,哪怕在静态方法里面,每一次 class.fangfa(),都会创建一次,反复创建只会增加内存,写一个静态变量,在静态的代码块里面创建这个复杂的东西,赋值给这个静态变量,最后在静态方法里面直接用这个静态属性就行了,不用调一次方法创建一次,因为静态的属性,代码块在类创建时就执行二了
class A{
static int a;
//a=5;//这种是错误的
static{
//在代码块里面可以进行一系列的赋值操作,静态代码块会在类创建时就执行一次
int s=2;
a= s*3/(s+15);
}
}
final
英文翻译就是:最终的
final可以用来修饰的结构:类,方法,变量
//这个类是最终的,不能往下延续了,不能有子类了,不让其他类继承他
final class A{
final int cc=10;//修饰变量就把变量变成常量了,不能修改,必须初始化赋值
//如果final修饰的是属性,可以初始化赋值,构造器赋值,代码块赋值
final public void BB(){
//这个方法不能被重写,如果类没有final被子类继承,这个方法有final,子类不能重写
}
}
抽象类abstract
随着继承越来越多,类变得越来越具体。而父类应该是一个抽象的概念,不应该有实体,如下思维导图 ```java //动物类是抽象类,不能实例化,但是有构造器,便于子类实例化调用 abstract class Animal{ protected String name; protected int age;
//抽象方法,父类也不能实例化,关于动物的方法,吃也好,睡也好,应该是抽象的,没有方法体
//不同的动物怎么吃,怎么睡是不一样的,父类不能具体表示
//包含抽象方法的类一定是抽象类
//非抽象子类,必须重写父类全部抽象方法才能实例化,没重写完全部抽象方法,表示该子类还是一个抽象类
abstract protected void eat();
abstract protected void sleep();
}
<a name="BJJlg"></a>
# 接口interface
有时候必须从几个类中派生出一个子类,继承他们所有的属性和方法,但是,java不支持多重继承,有了接口,就可以实现多重继承了。接口当做干爹,有多个干爹。
看上面抽象的思维图,老师,司机 都有 [赚钱工作] 的特征,但是生物类没有,子类人类也没有,不是所有的人都有赚钱的能力。把这种赚钱工作的特征,抽成接口,老师,司机同时实现这个接口,他们都有赚钱工作的特征,学生不实现,没有这个特征。
接口是一种规范,类需要 赚钱工作这种行为特征,就都需要实现 [赚钱工作] 这个接口,接口里面定义了一些规范,最低多少钱,每小时多少,提成比例,怎么赚等等,实现这个接口的类,都要遵循这些规范
接口和类一样,有多态性
```java
//接口和类是并列结构
//接口
interface Money{
//接口中不能定义构造器,不能实例化
//接口通过都是通过类来实现,和继承差不多的意思,但是他叫实现,实现接口的功能
//接口可以定义全局常量,书写时public static final可以不写
public static final int MIN = 1500;//最低工资
//抽象方法, public abstract可以不写
public abstract void do_work();
//静态方法,public可以省略,实现类不能直接使用这个静态方法,跟父类不一样
//只能通过接口调用 接口.methods() 或者自用
public static void methods(){
System.out.println("实现接口后不用重写这个方法");
}
//默认方法,可以通过实现类调用 实现类对象.methods2()
default void methods2(){
System.out.println("这个可以重写");
}
}
interface Teach{
String subject ="语文";
void do_teach();
}
abstract public class Person {
abstract public void eat();
abstract public void sleep();
}
//implements 用来实现接口,既然实现了接口,就必须重写完接口的全部抽象方法,
// 如果没有重写全部接口的抽象方法,那么这个类只能是抽象类
public class Teacher extends Person implements Money,Teach{
public void eat(){
System.out.println("老师在餐厅吃东西");
}
public void sleep(){
System.out.println("老师在家睡觉");
}
//实现接口的方法
public void do_work(){
System.out.println("老师赚钱了,赚了:"+Money.MIN);
}
public void do_teach(){
System.out.println("老师教书,教的是:"+Teach.subject);
}
}
//接口可以继承接口,多继承,多实现
interface AA extends Money,Teach{
}
接口多态例子(代理模式)
多学习思考这种思想
public class test1 {
public static void main(String[] args) {
Server server =new Server();//一个真实的服务器对象,可以当做就是你自己的服务器
//创建一个代理服务器对象,传入真实服务器对象,代理服务器设置好代理后,让真实服务器用设置好的代理上网
new ProxyServer(server);//形参是NetWork接口类型,而实参是Server实现类,这就是接口的多态性
}
}
//网络接口
interface NetWork{
//只要能连上网络接口,就有一个上网的功能
void shang_wang();
}
//服务器类,实现了网络接口,有上网的功能
class Server implements NetWork{
//服务器类里面的方法是用本服务器去真实的访问网络
public void shang_wang(){
System.out.println("真实的服务器访问网络");
}
}
//代理服务器,也实现了网络接口,有上网的功能
class ProxyServer implements NetWork{
private NetWork work; //真正联网的东西,实例化时传入初始化
public ProxyServer(NetWork work){
this.work=work;
}
public void setProxy(){
System.out.println("代理服务器设置代理");
}
//代理服务器里面的方法是用代理访问网络
public void shang_wang(){
setProxy();//访问网络之前,先设置代理
//设置了代理后,就开始联网的操作,但真正的联网不是代理服务器连接的
work.shang_wang();//让真正联网的东西去执行上网的操作
//我只是代理,帮你设置了代理ip等操作,我设置完了你自己联网,
//如果不经过代理服务器,自己联网,没有设置代理这一系列的步骤,能不能连上就是另一回事了,所以才叫代理
}
}