文章目录
OOP
类
对一类事物共有的特征的抽象描述
创建类
[权限修饰符] class 类名{
}
类的成员有:
1. 属性(成员变量)
2. 方法
3. 构造器
4. 代码块
5. 内部类
创建对象
类名 标识符 = new 类名();
属性(成员变量)
成员变量:在类中,方法外
当前类中都有效
每创建一个对象,就会分配一块独立的空间来存储相关成员变量的信息
- 实例变量(不加static修饰):
调用方法:对象名.属性名。
内存中占份数:每个对象都有一份。
内存中位置:堆中。
初始化的时间:创建对象后,就分配空间,有值。
销毁的时间:当没有引用指向时,会被垃圾回收器回收。 - 类变量(加static修饰):
调用:类名.属性名。
内存中占:当前类所有对象共享一份。
内存中位置:方法区。
初始化的时间:先有类,才会有对象,类变量的初始化时间早于实例变量。
销毁的时间:类变量的存活时间,随着类的消亡而消亡,存活时间相较于实例变量要长。 - 局部变量: 定义到方法中的变量
只能在当前方法内有效;
在使用前已经完成初始化操作,否则报错
局部变量在完成方法调用后才会完成初始化操作
当方法执行完毕,就会弹栈消失
使用final修饰 ,值不能改变
变量区别总结
- 声明位置和方法
- 静态变量:在类中方法外,并且有static修饰
- 实例变量:在类中方法外,没有static修饰
- 局部变量:在方法{}中或方法的形参列表、代码块中
- 在内存中存储的位置不同
- 静态变量:方法区
- 实例变量:堆
- 局部变量:栈
- 生命周期
- 静态变量:和类的声明周期一样,因为它的值是该类所有对象共享的,早于对象的创建而存在
- 实例变量:和对象的声明周期一样,随着对象的创建而存在,随着对象被垃圾回收而消亡,而且每一个对象的实例变量是独立的
- 局部变量:和方法调用的声明周期一样,每一次方法被调用而存在,随着方法执行的结束而消亡,而且每一次方法调用都是独立的
- 作用域
- 静态变量和实例变量:不谈作用域
在本类中,唯一的限制,静态方法或静态代码块中不能使用非静态的,其他都可以直接使用。
在其他类中,能不能使用看修饰符(public、protected,private等) - 局部变量:有作用域,除了作用域就不能使用
- 静态变量和实例变量:不谈作用域
- 修饰符
- 静态变量:public、protected、private、final、volatile等,一定有static
- 实例变量:public、protected、private、final、volatile、transient等
- 局部变量:final、
public、protected、private:权限修饰符
volatile:和多线程有关
transient:是否序列化,和IO有关
- 默认值
- 静态变量:有默认值
- 实例变量:有默认值
- 局部变量:没有,必须初始化
其中的形参比较特殊,靠实参给它初始化
方法
也叫函数,是一个独立功能的定义,是一个类中最基本的功能单元。
把一个功能封装为方法的目的是,可以实现代码重用,从而减少代码量。
静态方法:被static修饰的方法
非静态方法:不被static修饰的方法
方法的定义(声明)、调用:
[权限修饰符] [static] 返回值类型 方法名(形参列表){}
1. 只声明不调用是不会执行的
2. 静态方法使用:类名.方法名 进行调用,如果在本类中可以省略类名
3. 方法的执行顺序与声明顺序无关,只与调用顺序有关
4. 方法可以在任何方法中被调用
5. 方法执行完毕之后,会回到方法的调用处
6. 非静态方法:不在本类中,必须通过对象名.方法名();调用
7. 非静态方法:在本类中,可以省略对象名直接调用
8. 静态方法不能【直接】引用非静态的方法或者变量,可以通过创建对象的方式间接引用
9. 非静态方法来者不拒,静态或非静态的变量、方法都可以直接使用
static:静态的内容已经随着类的加载而加载,而此时对象可能还没有创建,所以不能使用,对象创建后才分配内存空间
虚方法
可以重写的方法
非虚方法
不可以重写的方法(静态方法、独有方法、使用final修饰的方法、构造器)
递归
指在当前方法内调用自己的这种现象
- 直接递归:方法自身调用自己
//求1~100的和
class Test{
public static void main(String[] args){
int sum = getSum(100);
System.out.println("sum=" + sum);
]
public static int getSum(int num){
//出口
if(num == 1){
retuen 1;
}
//不断地向出口靠近
return num + getSum(num-1);
}
}
- 间接递归:A调B,B调C,C调A
- 注意事项
- 递归一定要有条件限制,保证递归能够停止下来,否则会发生栈内存溢出
- 在递归中虽然有限制条件,但是递归次数不能太多,否则也会发生栈内存溢出
包
对代码进行分类管理
- 包名:一般采用公司网址的倒叙,所有字母小写(规范)
- 不同包下可存在同名类
java.lang 不需要导包
java.sql 数据库相关
java.io IO
java.net 网络编程相关
java.util 工具包
注:只要使用不是本包下的资源,都需要导包
3. 当一个类中需要使用同名的两个类,必然会有一个类使用全路径(全类名)导入,即包名+类名
4. 包名的声明,必须放在代码首行
可变形参
参数的个数可以任意个[0,n]
可变形参采用数组存储实参
一个方法可变参数只能有一个,且必须位于参数列表最后
声明方法:
public class Test{
public static void main(String[] args){
sum(1,2,3,4,5,6,7,8,9);
sum1(3.14,1,2,3,4,5);
}
static void sum(int...a){
for(int i = 0; i < a.length; i++){
System.out.println(a[i]);
}
}
static void sum1(double b,int...a){
double sum = 0;
for(int i:a){//求数组内的和
sum += i;
}
sum += b;//求数组内的和+double
System.out.println(sum);
}
}
方法重载
不同方法使用相同方法名
- 同一类中,同一方法名,不同的形参列表:数量、顺序、类型
- 方法调用时会根据不同的数据类型自动找到最佳匹配的方法
public class Test{
public static void main(String[] args){
sum(1,2);
sum(1.2,1.3);
}
static void sum(int a,int b){
System.out.println(a+b);
}
static void sum(double a,double b){
System.out.println(a+b);
}
}
命令行参数
- run --> eidt configurations -->main class 是自己运行的那个类 --> program arguments
- dos窗口 java Test 值1 值2
封装
属性不能直接暴露给外界访问
通俗讲:该隐藏的隐藏,该暴露的暴露
高内聚:类的内部数据操作细节字节完成,不允许外部干涉
低耦合:仅对外暴露少量的方法用于使用
外部类只能由public和default(什么都不写)修饰
修饰符 | 本类 | 本包 | 其他包子类 | 其他包非子类 |
---|---|---|---|---|
private(私有的) | √ | × | × | × |
缺省 | √ | √ | × | × |
protected (受保护的) | √ | √ | √ | × |
public(公共的) | √ | √ | √ | √ |
/*
属性不能直接暴露给外界访问
getXxx()方法进行属性赋值
setXxx()方法获取属性值,方法带返回值,返回值的类型与属性的类型保持一致
this:代表当前对象,谁调用就是谁
*/
public class Person{
//属性私有后,不能直接被访问
private String name;
private String sex;
private int age;
//给成员变量赋值
public void setSex(String sex){
this.sex = sex;
}
//获取成员变量的值
public int getSex(){
return sex;
}
public void setAge(int age){
if(age > 1 && age <= 120){
//当年龄满足要求的时候,直接赋值
this.age = age;
}else{//不满足要求
this.age = 0;
}
}
public int getAge(){
return age;
}
}
public class PersonTest{
public static void main(String[] args){
Person p1 = new Person();
p1.setAge(18);
p1.setSex("男");
System.out.println("性别:"+p1.getSex()+"年龄:"+p1.getAge());
}
}
构造器
快速给成员变量赋值
- 每一个类中都会有一个默认的无参构造器,当声明有参的构造器后,默认的无参构造器就会消失
- 建议自定义类都手动提供一个无参构造器
- 当成员变量与局部变量的名字重复的时候,在成员变量前+this,用于区分:我是成员变量
- 构造器(构造函数/构造方法)是可以重载的
- 可以用来调用本类中的构造器(根据数据类型进行匹配)(一般多参调少参)
- 调用本类构造器必须位于首行
- this可以调用成员方法,一般在本类中可以省略this
- 每一个构造器的首行,都会有一个默认的隐藏的super();调用父类无参的构造器
- 当出现this()调用本类中构造器时,那个默认的super()自动消失
- 可以使用super(参数)调用父类有参的构造器
- 可以使用super.去调用父类的方法和属性
public class Person{
private String name;
private String sex;
private int age;
private double height;
//手动添加无参构造
public Person(){
}
//语法:[权限修饰符] 类名(形参列表){}
//3. 当成员变量与局部变量的名字重复的时候,在成员变量前+this,用于区分:我是成员变量
public Person(String name,String sex,int age){
this.name = name;
this.sex = sex;
this.age =age;
}
public Person(String name,String sex,int age,double height){
//5. 可以用来调用本类中的构造器(根据数据类型进行匹配)
//6. 调用本类构造器必须位于首行
this(name,sex,age);
this.height = height;
}
public void show(){
//7. this可以调用成员方法,一般在本类中可以省略this
showInfo();
}
public void showInfo(){
System.out.println("姓名:"+name+",性别:"+sex+",年龄:"+age+",身高"+height);
}
}
public class PersonTest{
public static void main(String[] args){
Person p1 = new Person("张三","男",18);
p1.showInfo();
Person p2 = new Person("张三","男",18,1.83);
p2.showInfo();
}
}
继承
- 使用继承要借助关键字extends
- 语法结构:[权限修饰符] 子类 extends 父类{}
- 当子类继承父类后就可以使用父类的资源(属性和方法)
- this:代指当前对象,当前对象是谁,谁调用就是谁
- super:代表从父类继承过来的资源
- 当子类没有此资源而父类有时,则使用super 和this 没有区别
- 子类在使用资源时(属性、方法)优先在本类中查找,当本类中没有时,才会去父类中查找;一点一点向上找,知道找到Object类
- Object类是所有类的父类,如果一个类没有显示的继承另一个类,那么此类默认继承自Object类
- 如何快速显示继承关系:在需要查看的类上右键–>Diagrams–>show diagrams(可以通过 空格 添加想要查看的类)
重写
- 当父类的方法不能满足子类的需求时,要进行方法的重写
- 当子类重写父类的方法是时,访问权限不能比父类方法的访问权限更加严格(>=父类访问权限)
- 返回值类型
- 当父类的返回值类型是基本类型数据时,则子类必须与父类保持一致
- 当父类的返回值类型是引用类型数据时,则子类可以是返回父类,也可以返回子类
- 方法重写时,子类的形参必须与父类的形参保持一致,否则就相当于在子类中新增一个方法
- 关于异常:子类不能抛出比父类更大的异常(编译时异常有效)
/*
声明一个动物类
用于存储各种动物相同的信息
*/
public class Animal{
String name = "Animal";
int age = 18;
public void eat(){
System.out.println(name+"在吃饭");
}
public void call(){
System.out.println("叫");
}
}
//2. 语法结构:[权限修饰符] 子类 extends 父类{}
public class Dog extends Animal{
public void lookHome(){
System.out.println(name+"看家护院");
}
//1. 当父类的方法不能满足子类的需求时,要进行方法的重写
//方法的重写
public void eat(){
System.out.println("吃骨头");
}
}
public class Cat extends Animal{
int age = 10;
public void catchMouse(){
System.out.println(name+"抓老鼠");
}
public void showAge(){
//5. super:代表从父类继承过来的资源 this.age 10 18
System.out.println("cat的年龄:"+age+",Animal的年龄:"+super.age);
}
pubilc void showName(){
//6. 当子类没有此资源而父类有时,则使用super 和this 没有区别
//this:代指当前对象,使用的是从父类继承的资源
//super:直接使用父类资源
System.out.println("name: "+ super.name);
}
}
public class Test{
public static void main(String[] args){
Cat cat = new Cat();
cat.name = "Tom";
cat.,showAge();
cat.eat();
Dog dog = new Dog();
//8. 子类在使用资源时(属性、方法)优先在本类中查找,当本类中没有时,才会去父类中查找
dog.eat();
dog.call();
}
}
代码块
- 成员代码块:用于给成员变量赋值
每创建一个对象就会执行一次
早于构造器执行
按照代码块的书写顺序执行
public class Person{
String naem;
int age;
public Person(){
System.out.println("无参构造器");
}
//成员代码块
{
name = "张三";
age = 18;
System.out.println("代码块1");
}
{
name = "张三";
age = 18;
System.out.println("代码块2");
}
}
public class TestPerson{
public static void main(String[] args){
Person p = new Person();
//每创建一个对象就会执行一次
//早于构造器执行
}
}
- 静态代码块:用于给静态变量进行赋值
静态代码块只会创建一次,再次创建n个对象也不会再执行
先于构造器执行
按照代码块的书写顺序执行
public class Student{
static int age;
//静态代码块
static{
age = 20;
System.out.println("静态代码块1");
}
public Student(){
System.out.println("无参构造器");
}
static{
System.out.println("静态代码块2");
}
}
public class Test{
public static void main(String[] args){
Student s1 = new Student();
//静态代码块只会执行一次
Student s2 = new Student();
}
}
成员变量初始化
给成员变量赋值的方法
- 默认值
- 直接赋值
- 代码块赋值
- get/set
- 构造器赋值
静态变量:不建议采用构造器赋值,他是属于类的,可以使用“类名.属性”进行赋值
类的初始化
- 目的:为类中的静态变量进行赋值
- 类的初始化过程在调用一个()方法,而这个方法是编译器自动生成的,编译器会将(1)静态类成员变量的显式赋值语句(2)静态代码块中的语句。这两部分的所有代码,按顺序合并到类的初始化()方法体中
- 整个类初始化只会进行一次,如果子类初始化时发现父类没有初始化,会先初始化父类
实例初始化
- 目的:为类中非静态成员变量赋值
- 编译时自动处理代码,整理出一个()的类初始化方法,还会整理处一个或多个(…)实例初始化方法。一个类有几个实例化方法,由这个类就有几个构造器决定
- super()或super(实参列表)这里选择哪个,看原来构造器首行是哪句,没写,默认就是super()
- 非静态实例变量的显式赋值语句
- 非静态代码块
- 对应构造器中的代码
- 特别说明:其中 2 和 3 是按顺序合并的,1 一定在最前面 ,4 一定在最后面
- 执行特点:
- 创建对象时,才会执行
- 每new一个对象,都会完成该对象的实例初始化
- 调用哪个构造器,就执行它对应的实例初始化方法
- 创建子类对象时,父类对应的实例初始化会被先执行,执行父类哪个实例初始化方法,看用super()还是super(实参列表)
多态
多态:一个对象的多种状态
作用:可以让代码更加灵活
- 实现多态条件
- 有继承
- 有方法的重写
- 父类的引用指向子类的对象
- 多态有两种状态
- 编译时状态:=左边
- 运行时状态:=右边
- 多态创建的对象,能够调用什么方法,要看编译时状态
多态三个点
4. 应用的到形参
5. 多态数组:父类[] arr = {父类对象,子类对象}
6. 多态应用于方法的返回值:public 父类类型 方法名(){return 子类对象();}
public class Animal{
public void eat(){
System.out.println("在吃饭");
}
public void sleep(){
System.out.println("睡觉");
}
}
//1. 有继承
public class Dog extends Animal{
public void lookHome(){
System.out.println("看家护院");
}
//2. 有方法的重写
public void eat(){
System.out.println("吃骨头");
}
}
public class Cat extends Animal{
public void catchMouse(){
System.out.println("抓老鼠");
}
public void eat(){
System.out.println("吃鱼");
}
}
public class Test{
public static void main(String[] args){
//3. 父类的引用指向子类的对象
// 编译时状态 = 运行时桩体
Animal a = new Dog();
//3.多态创建的对象,能够调用什么方法,要看编译时状态
a.eat();//子类重写父类的方法。
//表面上调用的是Animal的eat()方法
//实际上运行的是Dog对象
//所以在执行时,执行的是Dog中eat()方法
a.sleep();//从父类继承的方法
Dog dog = new Dog();
showEat(dog);
Cat cat = new Cat();
showEat(cat);
//创建Animal类型的数组
//存储dog和cat
Animal[] animalArr = new Animal[2];
animal[0] = dog;
animal[1] = cat;
for(Animal a:animalArr){
a.eat();
}
}
//Animal animal = dog;
public static void showEat(Animal animal){
//表面上调用的是Animal的eat()方法
//实际上运行的是Dog对象
//所以在执行时,执行的是Dog中eat()方法
animal.eat();
}
}
多态的向上转型和向下转型
- 运行时类型从始至终不会发生改变
- 多态的向上转型和向下转型都是针对编译时类型
- 向上转型 up casting :Animal animal = new Cat();
弊端:不能使用子类所独有的资源 - 向下转型 down casting:使用子类自己独有的资源的时候
前提:已经完成了向上转型
- 向上转型 up casting :Animal animal = new Cat();
- 不是所有的类型都可以相互转换,借助于instanceof关键字进行判断
public class Animal{
public void eat(){
System.out.println("在吃饭");
}
public void sleep(){
System.out.println("睡觉");
}
}
public class Cat extends Animal{
public void catchMouse(){
System.out.println("猫抓老鼠");
}
public void eat(){
System.out.println("猫吃鱼");
}
}
public class Dog extends Animal{
public void lookHome(){
System.out.println("狗看家");
}
public void eat(){
System.out.println("狗吃肉");
}
}
public class Test{
public static void main(String[] args){
//1. 向上转型 up casting :Animal animal = new Cat();
// 弊端:不能使用子类所独有的资源
Animal animal = new Cat();
animal.eat();
//2. 向下转型 down casting:使用子类自己独有的资源的时候
// 前提:已经完成了向上转型
Cat cat = (Cat)animal;
cat.catchMouse();
}
}
instanceof
- 判断左边的对象是否属于右边的类型
- 小的对象可以是属于大的类型,必须有继承关系
public class Animal{
public void eat(){
System.out.println("在吃饭");
}
public void sleep(){
System.out.println("睡觉");
}
}
public class Cat extends Animal{
public void catchMouse(){
System.out.println("猫抓老鼠");
}
public void eat(){
System.out.println("猫吃鱼");
}
}
public class Dog extends Animal{
public void lookHome(){
System.out.println("狗看家");
}
public void eat(){
System.out.println("狗吃肉");
}
}
public class Test{
public static void main(String[] args){
Animal animal = new Animal();
Cat cat = new Cat();
System.out.println(animal instanceof Animal);//true
System.out.println(cat instanceof Animal);//true
String s = "你好";
System.out.println(s instanceof Object);//true
//System.out.println(s instanceof Animal);报错
Dog dog = new Dog();
//System.out.println(dog instanceof Cat);报错
showEat(dog);
showEat(cat);
}
public static void showEat(Animal animal){
animal.eat();
if(animal instanceof Cat){
Cat cat = (Cat)animal;
cat.catchMouse();
}
if(animal instanceof Dog){
Dog dog = (Dog)animal;
dog.lookHome();
}
}
}