一、面向对象编程
1、面向过程编程思想(Process Oritented Programming)
将实现一个功能的一组指令组合在一起,成为一个函数。这个函数就能实现这一个功能,是对功能实现的一种抽象。通过这种抽象方式,将代码实现复用。
自顶向下的编程思想
优点:
性能高,只针对功能的实现,里面传什么参数处理,且没有什么类实例化机制,性能相对于面向对象的话 要高。多用于单片机、嵌入式开发、Linux/Unix等一般面向过程开发。
缺点:
命名冲突
没有面向对象编程思想易于维护、易复用、易扩展。
2、面向对象编程思想(Object Oritented Programming)
对象:万物皆可以对象。
面向对象分析(OOA:Object Oriented Analysis):把构成问题域的事务分解成各个对象,给对象赋予相应的属性和行为,通过多个对象的相互协作来解决问题。
面向对象设计(OOD:Object Oriented Design):在面向对象分析的基础上,针对具体的实现平台,运用面向对象概念进行系统设计,建立一个可以在该平台上实现的面向对象的设计模型。
面向对象编程(OOP:Object Oritented Programming):根据程序设计,定义各种应用的类,为类增加属性和方法,将类创建成对象,各对象之间调用,发送消息,进行协作,完成系统功能。
优点:
易维护、易扩展、易复用、使用 封装、继承、多态(所有面向对象编程语言的特点),系统设计更加灵活,代码重用率非常高,提高软件开发效率。
缺点:
因为调用类进行实例化,开销较大,消耗系统资源,性能比面向过程要低。
实际上就是把具体的实例抽象成一个模型,模型拥有其相应的属性,一个人他有 姓名、年龄、身高、体重......等等属性;也拥有具体的类,比如行走(走多少步)、吃饭(吃什么)、运动(做什么运动)等等。
例子:
public class Car {
//根据对象抽象出类
//将共同的特点、特征,定义为类的属性
String color; //颜色
String brand; //车牌
int door_num; //门的个数
//将对象共同的行为定义为类的方法。
void strart(){
//启动
}
void speed(){
//加速
}
void stop(){
//停止
}
}
对比:
面向过程:系统以过程为中心来组织,函数间相互发送数据,函数的执行动作和数据被明显分离,关注的焦点在于数据结构、算法、执行步骤,一般难以复用。
面向对象:系统一对象为中心来组织,对象间相互发送消息,相关的属性和行为被统一到一对象上,关注的焦点是对象和对象的功能,系统构建更容易,易维护、易扩展、易复用。解决问题的思想更接近人的思维方式,更容易理解。、
二、类
1、类的定义
把相似的对象划分了一个类。
类指的就是一种模板,定义了一种特定类型的所有对象的属性和行为
在一个.java的问题件中,可以有多个class,但是智能有一个class是用public的class。被声明的public的class,必须和文件名相同。
[访问修饰符] class 类名{
成员变量-属性
成员方法-行为
内部类
代码块
}
public class Car {
String color;
String brand;
int door_num;
public void start(){
//启动
System.out.println(color+"颜色"+door_num+"门"+brand+"车正在启动......");
}
public void speed(){
//加速
System.out.println(color+"颜色"+door_num+"门"+brand+"车正在加速......");
}
public void stop(){
//停止
System.out.println(color+"颜色"+door_num+"门"+brand+"车正在停止......");
}
}
2、成员变量
java类的成员变量,都有默认值。
整数:0
浮点数:0.0
布尔:false
字符:'\u0000'
引用:null
java类的成员变量,在下面所有的方法中,都可以访问到。
3、局部变量
但是局部变量是没有默认值的。
原因:
JVM通过CLassLoader类加载器及进行类加载,过程设计到 加载,准备、解析和初始化,类的成员变量初始化,是在JVM类加载的阶段完成。
区别:
成员变量:静态(static)成员变量、非静态成员变量(初始化阶段赋值,根据代码中的赋值情况,如果没有赋值,则使用默认值,有赋值,则使用代码中的赋值;当当对象实例化后,成员变量会随着对象分配到Java堆内存中)。
局部变量:存储在栈内存中
4、对象
4.1 对象的定义
类是模板,通过模板创建出具体的实例,就是模板。
对象就是实际存在的某类事务的个体,也叫实例。
类和对象的关系,就是抽象和具体的关系。
创建对象的属性和方法:
类名 对象名=new 类名
4.2 对象的使用
public class test1 {
public static void main(String[] args) {
Car car1 = new Car();
car1.color="red";
car1.brand="baoma";
car1.door_num=4;
car1.start();
car1.speed();
car1.stop();
}
}
4.3 对象创建的原理
①JVM运行时包括:程序计数器、Java栈、堆、本地方法栈、方法区
②当我们创建一个对象时候,同时操作了栈内存和堆内存
③在栈内存保存了这个对象的引用
④对象的属性保存在对内存中。
⑤操作对象都是通过引用来完成,一旦引用出栈,没有被引用的对象就变成了垃圾,被回收。
在编写类文件的时候,类是存储在硬盘上的,没有被加载到内存中。
在主方法中创建了这个类的对象后,它才会被加载到内存中进行处理。
案例:
public class Clock {
int hour;
int mintue;
int second;
String timeZone;
public void showTime(){
System.out.println("现在是"+timeZone+"时间"+hour+"时"+mintue+"分"+second+"秒");
}
}
public class TestColck {
public static void main(String[] args) {
Clock clock1 = new Clock();
clock1.hour=11;
clock1.second=12;
clock1.mintue=22;
clock1.timeZone="beijing";
clock1.showTime();
Clock clock2 = new Clock();
clock2.hour=12;
clock2.second=12;
clock2.mintue=22;
clock2.timeZone="dongjing";
clock2.showTime();
}
}
4.4 匿名对象
new Car().speed();
就调用一次。
5、封装(属性的私有访问)
①Java的封装就是信息隐藏技术。从属性的角度来说,有选择的提供数据。
②封装也涉及到了软件的耦合性:高内聚,低耦合。
③将属性私有处理,需要使用private关键字;
④使用private关键字来修饰的属性,只能在当前类中访问。
⑤对于私有属性,一般会提供公有的访问方法:getter setter。 //驼峰式编码风格
Clock类
public class Clock {
private int hour;
private int mintue;
private int second;
private String timeZone;
public void setHour(int hour){ //局部变量
//在当前方法中,出现了两个hour,一个是成员变量,一个是局部变量
//使用this关键字来菊粉,加this 的表示是成员变量
this.hour=hour;
}
public void setMintue(int mintue){
this.mintue=mintue;
}
public void setSecond(int second){
this.second=second;
}
public void setTimeZone(String timeZone){
this.timeZone=timeZone;
}
public void showTime(){
System.out.println("现在是"+timeZone+"时间"+hour+"时"+mintue+"分"+second+"秒");
}
public int getHour(){
return hour; //this.hour 也可以这样写
}
public int getMintue(){
return mintue;
}
public int getSecond(){
return second;
}
public String getTimeZone(){
return timeZone;
}
}
Clock类的调用
public class TestColck {
public static void main(String[] args) {
Clock clock1 = new Clock();
clock1.setSecond(11);
clock1.setTimeZone("beijing");
clock1.setHour(12);
clock1.setMintue(13);
clock1.showTime();
//只想知道当前的小时和分钟
//很明显这样写不可以
System.out.println(clock1.hour+"时"+clock.mintue+"分");
//提供Get方法返回对应的值
System.out.println(clock1.getHour()+"时"+clock1.getMintue()+"分");
}
}
但是这样定义太麻烦了,我们就定义了四个属性,如果不加private,那么就可以直接访问,现在我们加上,就要弄八个方法,这么多太麻烦了。
那么可以用简单的方式:
快捷键:按住alt+insert
直接生成。
这个getter和setter的写法也叫属性暴露!
6、构造方法
6.1 构造方法的理解
① 也叫作构造器,严格来讲,构造方法不是方法。
② 构造方法要求和类名相同,没有返回值类型,也没有void.
③可以有return,表示方法的结束,不能返回值(不推荐),除非要写一个业务 逻辑
作用:
①完成对象的初始化工作
②每个Java的类都至少要有一个构造方法,如果不写构造方法,编译器会提供一个没有任何参数和语句的构造方法,Student(){},如果写了构造方法,那么编译器不会再提供默认的构造方法。 所以一旦自己写了构造方法,必须要写对,写正确。
③ 一个类可以定义多个构造方法(构造方法的重载)
④一般情况下都会写一个无参的构造方法。这是好的变成习惯。
public class Student {
Student(){
}
}
构造的作用就是完成对象的初始化工作,在这里,如果把Student()删掉
public class Student {
private String name;
private String gender;
private int age;
Student(){
name="张三";
gender="男";
age=19;
}
public void showInfo(){
System.out.println("姓名:"+name+"\t性别:"+gender+"\t年龄:"+age);
}
}
那么在这里输出的默认值就不会是张三 男 19
public class TestStudent {
public static void main(String[] args) {
//new Student()实际上就是在调用构造方法
Student student1=new Student();
student1.showInfo();
}
}
6.2 一个类可以定义多个构造方法(构造方法的重载)
private String name;
private String gender;
private int age;
public Student(){
System.out.println("这是无参数的构造方法!");
}
public Student(int x){
System.out.println("这是有参数的构造方法!");
}
public Student(String x){
System.out.println("这是有参数的构造方法!");
}
6.3 构造方法的调用
① 如果是在其他类中,则通过new的方式来调用构造方法
②如果在自己的类中,可以调用别的构造方法,但也仅限于构造方法间的调用,且必须写在第一行,不允许相互调用。
public Student(){
this(12);
System.out.println("这是无参数的构造方法!");
}
public Student(int x){
this("123");
System.out.println("这是有参数的构造方法!");
}
public Student(String x){
System.out.println("这是有参数的构造方法!");
}
6.4 构造方法的应用
那么既然构造方法也可以传值,是不是那些setter和getter都没用了?
非也,构造方法只是用来做初始化,之后还可能变化。
public class Dog_Test {
public static void main(String[] args) {
Dog dog1=new Dog("red",12);
dog1.showInfo();
dog1.setAge(13);
dog1.setColor("blue");
dog1.showInfo();
}
}
注意:一般情况下都会写一个无参的构造方法。这是好的变成习惯。
public Dog(){ //无参
};
public Dog(String color, int age){ //有参数
this.color=color;
this.age=age;
}
6.5 快捷生成构造 alt+insert
6.6 对象作为方法参数
讲对象作为方法参数:将所有属性进行封装,传递给另外一个类的方法,需要那些属性,获取那些属性。
老师
public class Teacher {
public void sign(Student student){ //传的是整个对象,可以到到这个人的 所有信息
System.out.println("老师点名!");
if (student.getGender().equals("男")){
System.out.println(student.getName()+"到");
} else if (student.getGender().equals("女")) {
System.out.println(student.getName()+"有");
}
}
}
学生
public class Student {
String name;
int age;
String gender;
public Student() {
}
public Student(String name, int age, String gender) {
this.name = name;
this.age = age;
this.gender = gender;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getGender() {
return gender;
}
public void setGender(String gender) {
this.gender = gender;
}
public void showInfo(){
System.out.println();
}
}
测试
public class Test {
public static void main(String[] args) {
Student student1=new Student("李清照",12,"女");
Teacher teacher=new Teacher();
teacher.sign(student1);
Student student2=new Student("杜甫",121,"男");
teacher.sign(student2);
}
}
6.7 方法返回类型是一个对象类型
Monitor
public class Monitor {
public Student register(){ //学生对象
Scanner scanner=new Scanner(System.in);
System.out.println("请输入姓名:");
String name = scanner.next();
System.out.println("请输入性别:");
String gender = scanner.next();
System.out.println("请输入年龄:");
int age = scanner.nextInt();
return new Student(name, age, gender); // 返回的是一个对象
}
}
public class Test {
public static void main(String[] args) {
Monitor monitor=new Monitor(); //测试的时候可以直接用Monitor,因为monitor返回的是对象student
Student student1=monitor.register();
Teacher teacher=new Teacher();
teacher.sign(student1);
Student student2=monitor.register();
teacher.sign(student2);
}
}
7、对象数组的方式
原来的方式调用很麻烦,每个对象都要用一行代码调用。
public class Test {
public static void main(String[] args) {
// 麻烦,需要调用很多次,建议用数组
Student student=new Student("李清照","女");
Student student1=new Student("李白","男");
Student student2=new Student("杜甫","男");
Teacher teacher=new Teacher();
teacher.sign(student);
teacher.sign(student1);
teacher.sign(student2);
}
}
原来的老师类
public class Teacher {
public void sign(Student student){
String gender = student.getGender();
if (gender.equals("男")){
System.out.println(student.getName()+"到!");
} else if (gender.equals("女")) {
System.out.println(student.getName()+"有!");
}else{
System.out.println("性别不对,没有"+gender);
}
}
}
看起来很麻烦,那么我们可以改成数组。
public class Teacher {
public void sign(Student[] students){
for (Student student : students) {
String gender = student.getGender();
if (gender.equals("男")){
System.out.println(student.getName()+"到!");
} else if (gender.equals("女")) {
System.out.println(student.getName()+"有!");
}else{
System.out.println("性别不对,没有"+gender);
}
}
}
}
测试
public class Test {
public static void main(String[] args) {
// 麻烦,需要调用很多次,建议用数组
Teacher teacher=new Teacher();
// 创建学生对象数组
Student[] students=new Student[3];
students[0]=new Student("李清照","女");
students[1]=new Student("李白","男");
students[2]=new Student("杜甫","男");
teacher.sign(students);
}
}
8、static关键字
8.1 类成员变量
使用static修饰,作用范围在整个类的所有对象上。所有对象共享这个变量,在内存中只有一个副本。访问通过类名.变量名访问,在类加载时被初始化,存储在堆里。
8.2 实例成员变量
没有static修饰,作用范围在某个实例对象上。在创建对象时被初始化,存在多个副本。各对象之间互不影响,通过实例对象名.变量名访问。
public class Person {
static String firstName; //类变量
String lastName; // 实例变量
public void showInfo(){
System.out.println(firstName+lastName);
}
}
public class Test_person {
public static void main(String[] args) {
Person person1 = new Person();
Person.firstName ="张"; //静态变量,类变量 通过类变量访问
person1.lastName="三"; //实例变量
person1.showInfo(); //张三
Person person2=new Person();
person2.lastName="四"; //实例变量
person2.showInfo(); //张四
}
}
8.3 Statick可以修饰方法
①静态方法 通过 类名.方法名()访问
1)使用static修饰,不能直接使用实例变量,只能使用静态变量。
2)若要使用实例变量,必须先创建实例,通过实例名.变量名访问。
3)只能直接调用静态方法,不能直接调用实例方法。
4)静态方法不允许出现this关键字,代表的是对象。
因为在使用实例变量时如果对象没有被创建,则没有初始化变量,就不能使用。
②实例方法 通过实例对象名.方法名()访问
1)没有static修饰,既可以用静态变量,也可以用实例变量。
2)既可以直接访问实例方法,也可以直接访问静态方法。
8.4 static可以修饰代码块
①执行顺序:静态代码块>构造代码块>构造方法
②静态代码块
是在类加载的时候就执行了,并且只执行一次,优先于各种代码块和构造方法,最开始执行。初始化
③构造代码块
在创建对象时被调用,每创建一次对象,都会执行一次构造代码块。执行顺序优先于构造方法执行。一个类如果有多个构造方法时, 每通过构造方法,创建一个对象,代码块都会被执行一次。但是对于构造方法来讲,只执行某一个构造方法。
④代码理解
public class Student {
static String name;
public Student(){
System.out.println("构造方法");
}
public Student(String args){
System.out.println("这是一个有参数的构造方法");
}
{
System.out.println("构造代码块");
}
static {
System.out.println("静态代码块");
}
}
public class TestStudent {
public static void main(String[] args) {
Student student=new Student();
Student student1=new Student("张三");
}
}
//静态代码块
//构造代码块
//构造方法
//张三开始执行
//构造代码块
//这是一个有参数的构造方法
⑤例子(统计创建多少个实例)
public class Teacher {
private static int count; //静态变量,在运行Teacher()时候都共享count这一个变量,count++
public Teacher(){
count++;
}
public static void showCount(){
System.out.println(count);
}
}
public class Test_count {
public static void main(String[] args) {
new Teacher();
new Teacher();
new Teacher();
new Teacher();
Teacher.showCount(); // 4
}
}
代码块是不管调用了那个构造方法,都会调用构造代码块。
{
count++; //构造代码块 适合统计创建了多少个实例
}
8.5 单例模式
设计模式:在某些项目环境下,针对某些问题,体统的一种通用的解决方案。
Gang of Four(GOF):23种软件设计模式。
单例模式:保证一个类只能创建一个实例对象,自行实例化。
实现:
1)构造方法私有
2)提供一个当前类的私有静态成员变量 (实例对象)
3)提供一个共有的静态方法,返回成员变量
① 构造方法私有化(饿汉式单例方法)
public class TestSing {
public static void main(String[] args) {
Single single=Single.getInstance(); //控制不能在外边创建类的实例对象
Single single1=Single.getInstance();
System.out.println(single1==single); //同一个对象
}
}
饿汉式,我这个人非常饿,一上来就要创建对象
public class Single {
private Single(){ //私有构造方法
}
//外边不能创建类的实例,就需要我们在类内创建一个实例
private static final Single single=new Single(); //静态常量
//返回实例
//通过类名去调用方法
public static Single getInstance(){ //静态方法不能使用实例变量
return single;
}
}
总结:
1)外边不能直接创建类的实例对象,只能通过类内创建实例对象,返回实例对象
2)通过静态方法返回实例对象,这样外边可以通过类方法去掉用
3)在类内创建对象时,需要用到静态常量创建对象,因为返回对象方法是静态的
②构造方法私有化(懒饿汉式单例方法)
懒汉式:我上来先不创建这个对象,我先做一个判断,如果这个对象是空值,那我就把它创建出来。(但是如果在同一个时间段发生了两个程序的并发,两个线程,都发现是空的,都去创建对象,就不能保证是单例模式,需要进行加锁)
public class Single2 {
private Single2(){}
private static final Single2 single2;
public static Single2 getInstance(){
if (single2==null){
single2=new Single2();
}
return single2;
}
}
java的Runtime方法也是饿汉式单例。
Runtime runtime
目的: 防止资源被多次加载。
回收站,进程的管理,都是用的单例模式。
9、使用访问修饰符实现封装
9.1 包的概念
package 进行打包操作
包的用途
1、避免命名冲突
2、可以按功能将类进行组合
3、可以保护类、数据和方法
注意问题:
1、package打包语句必须在代码的第一条语句。前面只有空白和注释
2、使用package时,会在磁盘上生成对应的目录
9.2 java语言中比较常用的包
java.lang //java语言包,不需要进行导报,就可以使用的
java.util //java使用工具包
java.io //java输入输出流包
java.net //java网络包
java.text //处理文本、时期、时间、数字、消息的类和借口
java.swing // 图形用户界面
java.sql //操纵数据库
9.3 导包操作
1)在同一个包中,类之间可以相互访问,可以不用导包操作。
2)如果类不在一个包下,需要进行import进行导包操作。
3)声明在package之后
自动导入->设置,常规,自动导入
如果没有明确的自动导入,则需要按Ctrl+1,进行选择。
9.4 静态导入
在一个类中反复的用到一个类提供的静态方法,就可以直接进行静态导入。
9.5 代码封装
属性和方法的访问修饰符。
正常处理的类是不可以用Static的。要么是public,要么是没有public。但是可以修饰属性和方法的。
//类访问修饰符的种类
default //不是用访问修饰符关键字来声明,表示默认,当前类和同包可以访问
public //共有的,所有类都可以访问,不管是否同包,没有public,只能在当前包访问
10、this关键字
this.属性名:区分全局属性和局部属性
this.方法名():
this([参数]):构造方法之间的调用
this:代表的是对象
11、继承
11.1 继承基本概念
继承:泛化(一般化):实现子类共享父类属性方法的机制。在已有类的基础上,扩展出新的类,在新的类中加入特殊的属性和方法。软件服用,提高代码开发效率。
// Audi
public class Audi extends CAR{
public void abs(){
System.out.println(brand+"\t"+color+"\t"+door_num);
start();
stop();
};
}
//BMW
public class BMW extends CAR{ //继承CAR
public void gps(){
System.out.println(brand+"\t"+color+"\t"+door_num);
start();
speed();
};
}
// 父类
public class CAR {
String color;
String brand;
int door_num;
String type;
public void start(){};
public void speed(){};
public void stop(){};
public void back(){};
}
11.2 可以继承父类中的哪些属性和方法
问题1:是否可以继承父类私有的属性和方法?
理解1:子类可以继承父类的所有属性和方法,只是访问权限的问题。
理解2:子类不能继承父类的私有属性的,但是如果子类中的共有方法影响到了父类的私有,那私有属性是可以被子类使用的。
总结:父类私有的属性在子类中不可以直接使用,必须通过getter 和setter方式进行访问。
问题2:是否可以继承父类默认的属性和方法?
可以继承,在同包下可以访问,在不同包下不可以访问。
问题3:是否可以继承和访问父类受保护的属性和方法?
protected,可以继承,在同包下也可以访问,在不同包下也可以访问。
问题4:是否可以访问和继承共有的属性和方法?
public, 可以继承,在同包下也可以访问,在不同包下也可以访问。
总结:私有的不可以访问,默认的在同包下可以访问,受保护的和共有的,可以在不同包访问,相当于在子类声明中一样使用。
父类:
public class CAR {
public String color; //公有属性
String brand; //默认情况不能访问
protected int door_num; // 受保护的属性
String type;
public void start(){ //公有的
System.out.println("前进");
};
void speed(){}; //默认情况,不同包下,可以继承,不能访问
protected void stop(){}; //受保护情况,不同包下,可以访问
public void back(){};
}
子类BMW:
public class BMW extends CAR {
public void gps(){ //子类的方法
System.out.println(brand+"\t"+color+"\t"+door_num);
start();
speed(); //不能访问speed,不同包下,默认的
};
}
子类Audi:
public class Audi extends CAR{
public void abs(){ //子类的方法
System.out.println(brand+"\t"+color+"\t"+door_num);
start();
stop();
};
}
11.3 子类实例化过程
①父类的构造方法,不能被子类继承
②在子类的构造方法中,调用了父类的构造方法。
public Audi(){ // 构造方法
super(); // 相当于执行了super();父类
System.out.println("子类构造方法调用");
}
③如果父类有无参的构造方法,则子类super()可以省略。
④如果父类没有无参的构造方法(有一个有参的构造方法),则子类super(参数)不可以省略。
⑤如果在子类中用super显示的调用父类的构造方法,则必须放在子类构造方法的第一行。
⑥子类构造方法中,不能同时出现this和super。
问题:
在子类创建对象时,是否也创建了父类的对象?
肯定是否定的,创建子类对象的时候