面向对象程序设计
面向对象(将程序模块化)
- 面向过程->开发效率低,维护成本高;面向对象->忽略过程中的细节,事物模块化
- 类和对象
类是产生对象的模板,是对象的抽象化描述,对象是类的具体实例
Java程序是以类为单元,程序运行时的主体是通过类创建的具体对象
定义类
public class 类名{ //定义属性,属性名(驼峰式) public 数据类型 属性名; //定义方法 public 返回值类型 方法名(数据类型 参数名){ //方法体 } }
构造方法(构造函数;构造器)
普通方法:描述动作
构造方法:创建对象
- 方法名与类名一致
- 不需要定义返回值类型
任何一个类都默认自带一个无参构造函数,如果手动在类中定义一个有参构造,则会覆盖默认的无参构造
class Student{ public String name; public int ID; public Student(String N,int i){//构造方法(有参构造) name=N; ID=i } public void ChangeName(String NewName){//普通方法 name=NewName; } }
调用方法
类 . 方法(参数列表); //例:teacher.lesson(work);
方法重载
在一个类中允许同名的方法(普通/构造方法)存在(参数声明不同——>数据类型不同;形参顺序不同;参数的数据类型相同但参数个数不同)(与参数名称和返回值类型无关)
//方便调用时可以灵活地创建出不同需要的对象
//重载的多个构造方法实际上就是相当于提供了多个初始化new对象的模板
方法的可变个数的参数
前提:可能要给方法传递不同个数的参数
将方法中的参数定义为String类型的数组,调用方法时传入字符串数组即可
没有参数可以不填
放在所有参数之后
public void PrintInfo(String...args){ // Java特有的方式,代表0到多个参数 for(int i=0;i<args.length>public class Test{ public static void main(String[] args){ Person p=new Person(); String[] a=new String[]{"1","woshidage","2333"}; p.PrintInfo(args); } }
方法的参数传递
方法必须有其所在类或对象调用才有意义,含有(形参:方法声明时的参数;实参:方法调用时实际传给形参的参数值)
Java例方法的参数传递只有值传递
如果方法的形参是基本数据类型,那么实参(实际的数据)向形参传递参数时,就是直接传递值,把实参的值复制给形参如果方法的形参时对象,那么实参(实际的对象),向形参传递参数的时,也是把值传给形参,这个值是实参在栈内存中的值,也就是引用对象在堆内存中的地址基本数据类型都是保存在栈内存中,引用对象在栈内存中保存的是引用对象的地址,那么方法的传递参数是传递值(变量在栈内存中的值)</args.length>
成员变量和局部变量
变量的作用域:在程序中可以通过变量名来访问该变量的范围,变量的作用域由变量被声明时所在的位置决定的。
Java中根据不同的作用域可以将变量分为成员变量和局部变量
局部变量:如果一个变量在方法中声明,则该变量时局部变量(作用域小,高优先级)
成员变量:如果一个变量在方法外,类中声明,则该变量是成员变量(作用域大,优先级低于参数列表和局部变量)
二者区别:作用域不同(成员变量的作用域在整个类中,类中的每个方法都可以访问该变量;局部变量的作用域只在定义该变量的方法中,出了方法体就无法访问)
初始值不同(局部变量不会赋初值,成员变量会赋初值,由数据类型决定)
包(package)
整理文件(作用):文件太乱不好管理、文件同名
package 顶层包名 . 子包名;(可以有多级)
包用小写单词,类名首字母大写
引用(import)
位于package下,使用定义在不同包中的Java类
使用方法:import 包 . 包 . 类(或*,代表包中所有类); //包 . 包 . 类 . 变量 = new 包 . 包 . 类();
封装
指将类的属性隐藏在内部,外部不能直接访问和修改(防止出现的变量不合常理)
为了保护变量信息,通过修改成员变量的可见性,从公有改为私有(public->private)
封装的核心思想就是把属性都隐藏在内部,对外提供方法来访问和操作,我们可以在这些方法中添加逻辑处理来实现过滤,以屏蔽错误数据的赋值
封装的步骤:
- 修改属性(成员变量)的访问权限为私有,使外部不能直接访问
- 提供外部可以直接调用的方法(方法为public类型,一般有setXxx和getXxx)
- 在方法中加入对于属性的逻辑控制,避免出现逻辑错误
访问权限:该属性可以被直接访问的范围,是在属性定义时设定的
(public、private、不写、protected)作用于范围不同
import java.util.Scanner; public class Person{ private int age; public void printAge(){ System.out.println("年龄是"+age); } public void setAge(){ System.out.print("请输入年龄:"); Scanner sc=new Scanner(System.in); int a; while(true){ a=sc.nextInt(); if(a <= 150 && a >= 0){ age = a; break; } else{ System.out.print("输入的年龄不符合规则,请重新输入:"); } } } }
this关键字
当形参与成员变量重名时,如果在方法内部需要使用成员变量,必须添加this来表明该变量时类成员
· 在任意方法内,如果使用当前类的成员变量或成员方法可以在其前面添加this,增强程序的阅读性
this用来指代当前类的实例化对象,通过this可以调用当前类的属性和方法(例:在有参构造中,通过this将外部传入的值赋给当前类的实例化对象)
this还可以在类中调用方法,用this调用构造/普通方法的语法不同(前:this(参数列表)->不能在普通方法中使用this调用构造函数;后:this.方法名(参数列表)->在构造函数和普通方法中都可调用)
this();//调用无参构造方法,实现同一个类中构造方法相互调用的特殊格式(禁止套娃:无法同时相互调用;调用自身)
JavaBean
JavaBean是指符合如下标准的Java类:
类是公有的(public)
有一个无参的公共的构造方法
有属性,且属性为private,有对应的set、get方法
快速定义set、get方法:
前提:写好private类型的属性
步骤:鼠标右键->Source->Generate Getters and Setters...->g
Static(全局)
static表示静态或者全局,可以用来修饰成员变量和成员方法以及代码块(可以不用对象而访问方法)
使用static修饰的成员变量和成员方法独立于该类的任何一个实例化对象,访问时不依赖于该类的对象,而是直接通过类访问(被该类的所有实例对象所共用)
static可以修饰代码块(静态代码块),特点:只执行一次(because类也只执行一次),当类被加载到内存时自动执行,不需要开发者手动调用
在子类的构造方法中,可以通过super访问父类的构造方法和普通方法
static{ System.out.print(1); }
继承
描述类之间的关系:一个类继承(拥有)另一个类的属性和方法(父类->子类)(基类——派生类)
把共性的东西抽象为父类,有需求的子类在继承父类的基础上写自己特有的代码(子类是对父类的"扩展")
基本语法:
public class 类名 extends 父类名{ //子类 is a 父类 }子类拥有父类和自己特有的属性和方法(不能继承父类构造方法)
Java中的继承时单继承,一个子类只能有一个父类----所有类都有一个共同父类Object
作用:
提高代码复用性
提高代码复用性
让类与类之间产生了关系,提供了多态的前提
继承存在逻辑关系,不要仅为了获取其他类中某个功能而去继承(例:人和狗具有一些相同的相同的属性,如名字、年龄、性别,但是狗不能继承人的类)
子类访问父类
创建子类对象时,会默认先创建一个父类对象,无论是通过有参构造或是无参构造来创建子类对象,都是默认通过无参构造来创建父类对象的
可以通过super关键字让子类创建对象时调用父类的有参构造
public Student(){ super(参数); System.out.println("通过无参构造创建了Student对象"); }
继承的传递性
多层继承:一个子类可以当成父类被继承(Object->父类->子类1->子类2.......)
super关键字
子类可以访问和调用父类的构造方法、普通方法、成员变量,都是通过super关键字来完成 通过super,子类可以调用所有父类层级
语法:
构造方法:super(); 普通方法:super.方法名(参数列表); 成员变量:super.成员变量名;
子类中所有的构造方法不能访问父类无参构造
在父类只有有参构造可以使用时,子类需调用父类的有参构造,并且调用父类构造方法要写在第一行
在子类的普通方法中,只能通过super访问父类的普通方法
子类不能直接访问父类中私有的(private)的成员变量和方法
在子类中使用构造方法
子类不能继承父类的构造方法。子类在创建新对象时,依次向上寻找其基类,找到最初的基类
执行最初的基类的构造方法,再依次向下执行派生类的构造方法,直至执行完最终的扩充类的构造方法为止
构造方法不能被继承,它们只属于定义它们的类
创建一个子类对象时,首先调用父类的构造方法,接着才执行子类构造方法
访问权限修饰符
private:类内部
default:类内部,同一个包
protected:类内部,同一个包,子类
public:任何地方
对于class的权限修饰只能用public和default(同一个Java文件中可以写多个class,但只有一个class能被public修饰)
如果子类和父类在同一个包下,子类可以使用父类非private修饰的成员
如果子类和父类不在同一个包下,子类只能使用父类中protected和public修饰的成员
方法重写(override)
子类在父类方法的基础上,对父方重新定义并覆盖的操作(同名覆盖)
规则:1.构造方法不能被重写
2.父子类的方法名、返回类型、参数列表相同
3.子类方法的返回值与父类方法的返回值类型相同或是其子类
4.子类方法的访问权限不能低于父类
方法重写-方法重载
位置:(在子类中对父类进行重写)(在同一个类中)
方法名:(均相同)
参数列表:(相同)(不同)
返回值:(相同或是子类)(没有要求)
访问权限:(不能小于父类)(没有要求)
多态(Polymorphism)
一个事物具有多种表现形态,在Java程序中,定义一个方法,在具体的生成环境中根据不同的需求呈现不同的业务逻辑
Java引用变量有两个类型:编译时类型(由声明变量时使用的类型决定)、运行时类型(由实际赋给变量的对象决定)
如果编译时类型和运行时类型不一致,就出现多态(对象的多态)
对象的多态
在Java中,子类对象代替父类对象使用,一个引用类型变量可以指向多种不同类型对象
Person P=new Student(); // 父类的引用对象指向子类的实例
Person P=new Person();
P=new Student(); //P指向Student类型的对象
· 子类可以看作是特殊的父类,所以父类类型的引用可以指向子类的对象:向上转型(upcasting)
一个引用类型变量如果声明为父类的类型,但实际引用子类对象,那么该变量就不能再访问子类中添加的属性和方法
虚拟方法调用
Person e=new Student();
e.getInfo(); //调用Student类的getInfo方法
// 编译时e为Person类型,而方法的调用是在运行时确定,所以调用的是Student类的getInfo()方法(存在于方法重写之上)
多态的具体使用
1.定义方法时形参类型为父类,实际调用方法时传入子类类型的参数
2.定义方法时返回值类型为父类,实际调用方法时返回子类对象
基本原理:父类引用可以指向子类对象
instanceof 操作符
x instanceof A :检验对象x是否为类A的对象,返回值为boolean型
要求:x所属的类与类A为父子类关系
如果x属于类A的子类B,x instanceof A也为true
Person e=new Student();
e instanceof Student; // true
Object类
Object类是所有Java类的根父类(基类)
如果在类的声明中未使用extends关键字指明父类,则默认父类为Object类
多层继承,处于最高层的父类一定是Object类
public class Test{ public void test(Object obj){ //不确定传进来的类,可以确定传递实参一定是个类 } public static void main(String[] args){ Test t=new Test(); Person p=new Person(); Student s=new Student(); t.test(p); t.test(s); } }
对象类型转换
基本数据类型转换
int i=20; double d=i; // 自动类型转换 long l=10l; int i=(int)l; // 强制类型转换
Java对象的强制类型转换(造型)
· 从子类到父类的类型转换可以自动进行
· 从父类到子类通过造型实现
· 无继承关系的引用类型间的转换非法
public void method(Person e){ if(e instanceof Student){ Student s=(Student) e; s.getschool(); }else{ e.test(); } }
==操作符和equals方法
基本类型比较值
int i=3; System.out.println(i == 4); // false引用类型比较:只有指向同一个对象时,== 才能返回 true (两边的数据类型必须兼容)
Person p1 = new Person(); Person p2 = new Person(); System.out.println( p1 == p2 ); // false
Person p1 = new Person(); Person p2 = p1; System.out.println( p1 == p2 ); // true
equals方法
所有类都继承了Object,获得 equals () 方法,可以重写
例:obj1 . equals( obj2 ) ; // 只能比较引用类型,比较是否指向同一个对象
String s1 = new String("abc"); String s2 = new String("abc"); System.out.println( s1 == s2 ); // false System.out.println( s1.equals(s2) ); // true对于对象来说,特殊的类,如String、File、Date,使用 == 比较的是对象的地址;equals ( ) 比较的是内容
除了特殊的类之外的其他普通的类的对象, == 和equals ( ) 比较的都是对象内存地址(可重写equals方法)
String类型的创建
// 字面量创建String对象 String s1 = "abc"; // 常量池添加"abc"对象,返回引用地址给s1 String s2 = "abc"; // 通过equals方法判断常量池已有,返回相同引用 System.out.println( s1 == s2 ); // true // new创建String对象,一定会创建新对象,地址不重复 String s3 = "x" + "y"; // 在常量池中添加"xy"对象 String s4 = new String("1") + new String("2"); // 在常量池中添加"1"和"2"两个对象,在堆中创建为"12"的对象,再引用地址
包装类
针对八种基本定义相应的引用类型 - 包装类(封装类)
boolean - Boolean
byte - Byte
short - Short
int - Integer
long - Long
char - Character
float - Float
double - Double
作用:
装箱:把基本数据类型包装成包装类
int i = 500 ; Integer t = new Integer( i ); Float f = new Float("4.56"); Long l = new Long("asdf"); // 报错
拆箱:获得包装类对象中包装的基本类型变量
// 调用包装类的.xxxValue()方法; Integer i = new Integer(111); // Integer i = 111 自动装箱然后赋值 System.out.println(i); // 111(自动拆箱然后输出) int i0 = i.intValue(); // 拆箱 boolean b = new Boolean( "false" ).booleanValue(); // boolean b = new Boolean( "false" ); 自动拆箱 Boolean B = true; // 自动装箱 System.out.println( b ); // false
// 主要应用: Integer i = Integer.parseInt("123"); Float f = Float.parseFloat("0.40"); Boolean b = Boolean.parseBoolean("false"); // 字符串转换成基本数据类型 String istr = String.valueOf(i); // 基本数据类型转换成字符串
toString
父类Object的toString作用:输出当前对象的内存地址
如果想要输出类的其他信息,需要重写toString方法
public String toString(){ String str = this.getName() + " " + this.getAge(); return str; } // toString方法重写,用于打印对象 public void toString(){ System.out.println( this.getName() + " " + this.getAge ); } // 另一种形式
static关键字
无论产生多少对象,static修饰的数据在内存空间中只有一份,可以被所有对象共享(不因对象不同而改变)
实例变量:只有实例化之后才能使用,属于实例化对象的一部分
类变量(静态变量):不用实例化,直接通过类名使用,属于类的一部分,被所以有类的实例化对象共享(例如与创建对象有关的计数方法)
常用:类方法作工具类(例如判断字符串是否为空的方法,这一类方法可以抽取到同一个类中形成工具类),不需要创建对象就可以调用,简化方法调用过程
特点:
随着类的加载而加载
优先于对象存在
修饰的成员被所有对象共享
访问权限允许时,可以不创建对象而直接被类调用
修饰的方法内部不能有this
public class Chinese{ public String name; private int age; public String country; // static String country; country为类变量(静态变量) public static void Test(){ System.out.println("这是一个静态方法"); } } public class Main{ public static void main(String[] args){ // Chinese.country = "China"; Chinese c1 = new Chinese(); c1.country = "China"; // 可以注释掉 Chinese c2 = new Chinese(); c2.country = "China"; // 可以注释掉 // System.out.println(Chinese.country); 直接通过类名设置和访问 } }
单例(Singleton)设计模式
单例:只有一个实例化对象(在整个软件系统运行过程中,这个类只被实例化一次,只调用这个实例)
设计模式:在大量的事件中总结和理论化之后优选的代码结构、编程风格、解决问题的思考方式
总结出的解决问题的套路
应用:实例化对象的创建需要消耗大量的时间和资源public class Single{ public Single(){ // 假设构造中执行复杂代码(耗时很长) } // 适合使用单例模式 }
实现方式—饿汉式
定义:在类中初始化定义一个静态实例化对象,之后实例化都是这个对象
public class Single{ private Single(){ } // 构造方法私有化,调用类时无法使用new创建对象 private static Single s = new Single(); // 私有的Single类型的类变量,只有一个 public static Single getInstance(){ return s; } // 使用已定义的类变量 }
实现方式—懒汉式
定义:最开始对象是null,直到第一个人调用,才new一个对象,之后调用的都是这个对象
public class Single{ private Single(){ // 私有化构造 } private static Single s = null; // 将s初始化为空 public static Single getInstance(){ if( s == null ){ s = new Single(); // 第一个实例化时定义对象 } return s; } }
理解main方法
public static void main(String[] args){
// 公有的、类方法、无返回值、方法名、传进字符串数组
}
由于Java虚拟机需要调用类的main()方法 -> 访问权限为public,不必创建对象 -> static,接收String类型的数组参数,保存执行命令时运行的参数
public class TestMain{ public static void main(String[] args){ for(int i=0,i<args.length>模板方法设计模式
抽象类是子类的通用模板把不确定的功能内部实现暴露出去让子类实现final关键字
final修饰的类无法被继承 // public final class Test { }final修饰的方法不能被子类重写 // public void Test () { }final修饰的变量称为常量,只能被赋值一次 // final static int I = 0; 定义时完成赋值,final和static共同修饰为全局常量// 常量定义名称使用大写,多个单词用 _ 连接 </args.length>
抽象类
没有具体实例(无法实例化)的父类,保证子类共享特征,抽象类是类的模板,属性和方法抽象化处理
abstract可以修饰类、方法,使之成为抽象类、方法 // 例:public abstract void Test ( ) ;
含有抽象方法的类一定是抽象类;抽象方法不一定含有抽象类;
抽象类的子类需要重写父类的抽象方法,并提供方法体,除非子类也是抽象类
接口
接口(interface)是抽象方法和常量值的定义的集合,实现多重继承的效果
接口是一种特殊的抽象类,只包含常量和方法的定义,没有变量和方法的实现,没有构造方法
实现接口类:
interface Interface1 { } // 方法默认为 public abstract ;属性默认为 public static final
public class Test implements Interface1 { }
一个类可以实现多个接口,接口可以继承其他接口
实现接口的类需要实现接口的所有方法,否则就要定义成抽象类
一个类既继承父类又实现接口,先extends再implements
泛型
泛型类
class A{ // 泛型T可以任意取名,一般使用T(type) private T key; public void setKey(T key){ this.key = key; } public T getKey(){ return this.key; } }
Aa = new A(); // 在new对象中指定泛型类型String a.setKey("xxx"); // 对象使用中key的类型为String String a1 = a.getKey(); // t.getKey返回值类型也是String
Aa2 = new A(); a2.setKey(1); Integer i = a2.getKey(); // <>中如果没写就是Object类型
泛型接口
public interface IB{ public T test(T key); } // 未传入实际泛型 public class B1implements IB{ public T test(T t){ return t; // B1b1 = new B1(); } } public class B2 implements IB{ public String test(String t){ return t; // B2 b2 = new B2(); } }
泛型方法
public class C{ publicvoid main(T s){ T t = s; } public String void test1(String s){ return s; } }