面向对象(Object Oriented)是软件开发方法。面向对象的概念和应用已超越了程序设计和软件开发,是一种对现 实世界理解和抽象的方法,是计算机编程技术发展到一定阶段后的产物。 面向对象是相对于面向过程来讲的,指的是把 相关的数据和方法组织为一个整体 来看待,从更高的层次来进行系 统建模,更贴近事物的自然运行模式。
面向过程到面向对象思想层面的转变:
面向过程关注的是执行的过程,面向对象关注的是具备功能的对象。 面向过程到面向对象,是程序员思想上 从执行者到指挥者的转变。 此概念如果直接去理解的话可能会比较抽象,因为大家缺少对原始的面向过程的开发语言的了解.
面向对象思想从概念上讲分为以下三种:OOA、OOD、OOP
OOA:面向对象分析(Object Oriented Analysis)
OOD:面向对象设计(Object Oriented Design)
OOP:面向对象程序(Object Oriented Programming)
封装性:所有的内容对外部不可见
继承性:将其他的功能继承下来继续发展
多态性:方法的重载本身就是一个多态性的体现
class 类名称{
成员属性
成员方法
}
class 类名称{ 成员属性 成员方法 }
属性定义格式:
数据类型 属性名;
属性定义并赋值的格式:
数据类型 属性名 = 初始化值;
方法定义格式:
权限修饰符 返回值类型 方法名(形式参数列表){
//方法体
return 返回值;
}
一个类要想真正的进行操作,则必须依靠对象,对象的定义格式如下:
类名称 对象名称 = new 类名称() ;
如果要想访问类中的属性或方法(方法的定义),则可以依靠以下的语法形式:
访问类中的属性: 对象.属性 ;
调用类中的方法: 对象.方法(实际参数列表) ;
一个类, 可以存在多个构造方法 :
参数列表的长度或类型不同即可完成构造方法的重载
构造方法的重载 ,可以让我们在不同的创建对象的需求下, 调用不同的方法来完成对象的初始化!
没有对象名称的对象 就是匿名对象。
匿名对象只能使用一次,因为没有任何的对象引用,所以将称为垃圾,等待被G·C回收。
只使用一次的对象可以通过匿名对象的方式完成,这一点在以后的开发中将经常使用到
栈
Java栈的区域很小,大约2m左右,特点是存取速度快
栈存储的特点是先进后出
存储的是:
基本数据类型的数据 以及 引用数据类型的引用!
例如:
int a =10;
Person p = new Person();
10存储在栈内存中 , 第二句代码创建的对象的引用(p)存在栈内存中
堆
存放的是类的对象
Java是一个纯面向对象语言, 限制了对象的创建方式:
所有类的对象都是通过new关键字创建
new关键字, 是指告诉JVM , 需要明确的去创建一个新的对象 , 去开辟一块新的堆内存空间:
堆内存与栈内存不同, 优点在于我们创建对象时 , 不必关注堆内存中需要开辟多少存储空间 , 也不需要关注内存占用 时长 !
堆内存中内存的释放是由GC(垃圾回收器)完成的
垃圾回收器 回收堆内存的规则:
当栈内存中不存在此对象的引用时,则视其为垃圾 , 等待垃圾回收器回收 !
例如:
Person p0 = new Person();
Person p1 = p0;
Person p2 = new Person();
观察如下代码:
class Person{
private String name ; // 表示姓名
private int age ; // 表示年龄
void tell(){
System.out.println("姓名:" + name + ";年龄:" + age);
}
};
public class Demo{
public static void main(String args[]){
Person per = new Person() ;
per.name = "张三" ;
per.age = -30 ;
per.tell() ;
}
};
以上的操作代码并没有出现了语法错误,但是出现了逻辑错误 (年龄-30岁)
在开发中, 为了避免出现逻辑错误, 建议对所有属性进行封装,并为其提供set及get方法进行设置和取得操作。
修改代码如下
class Person{
private String name ; // 表示姓名
private int age ; // 表示年龄
void tell(){
System.out.println("姓名:" + getName() + ";年龄:" + getAge()); }
public void setName(String str){
name = str ;
}
public void setAge(int a){
if(a>0&&a<150)
age = a ;
}
public String getName(){
return name ;
}
public int getAge(){
return age ;
}
};
public class OODemo10{
public static void main(String args[]){
Person per = new Person() ;
per.setName("张三") ;
per.setAge(-30) ;
per.tell() ;
}
};
在Java基础中,this关键字是一个最重要的概念。使用this关键字可以完成以下的操作:
· 调用类中的属性
· 调用类中的方法或构造方法
· 表示当前对象
static表示“静态”的意思,可以用来修饰成员变量和成员方法(后续还会学习 静态代码块 和 静态内部类)。
static的主要作用在于创建独立于具体对象的域变量或者方法
简单理解:
被static关键字修饰的方法或者变量不需要依赖于对象来进行访问,只要类被加载了,就可以通过类名去进行访问。
并且不会因为对象的多次创建而在内存中建立多份数据
重点:
普通代码块
在执行的流程中出现的代码块, 我们称其为普通代码块。
构造代码块
在类中的成员代码块, 我们称其为构造代码块, 在每次对象创建时执行, 执行在构造方法之前。
静态代码块
在类中使用static修饰的成员代码块, 我们称其为静态代码块, 在类加载时执行。 每次程序启动到关闭 ,只会 执行一次的代码块。
构造方法 与 构造代码块 以及 静态代码块的执行顺序:
静态代码块 --> 构造代码块 --> 构造方法
main()方法:
public static void main(String args[])
以上的各个参数的含义如下:
· public:表示公共的内容,可以被所有操作所调用
· static:表示方法是静态的,可以由类名称直接调用。java StaticDemo09 · void:表示没有任何的返回值操作
· main:系统规定好的方法名称。如果main写错了或没有,会报错:NoSuchMethodError: main
· String[] args:字符串数组,接收参数的
public class StaticDemo10{
public static void main(String args[]){
for(int i=0;i
单例设计模式是我学习的第一个设计模式,也是比较重要的一个设计模式,今天,我们就学习单例设计模式的两种实现方式。
单例设计模式:保证程序在内存中只有一个对象存在(被程序所共享)
单例设计模式的两种实现方式:
一、懒汉式:随着类的加载在内存中对象为null,当调用 getInstance 方法时才创建对象(延迟加载)
二、饿汉式:随着类的加载直接创建对象(推荐开发中使用)
单例设计模式的实现步骤:
1.保证一个类只有一个实例,实现方式:构造方法私有化
2.必须要自己创建这个实例,实现方式:在本类中维护一个本类对象(私有,静态)
3.必须向整个程序提供这个实例,实现方式:对外提供公共的访问方式(getInstance方法,静态)
懒汉式实现如下:
class Single{
private Single(){
}
private static Single s1 = null;
public static Single getInstance(){
if(s1 == null){
s1 = new Single();
}
return s1;
}
}
饿汉式实现如下:
class Single2{
private Single2(){
}
private static Single2 s = new Single2();
public static Single getInstance(){
return s;
}
void print(){
System.out.println("Hello World!");
}
}
class 父类{
}
class 子类 extends 父类 {
}
继承父类的成员变量、成员方法等
1.通过super, 可以访问父类构造方法
2.通过super,可以访问父类的属性
3.通过super,可以访问父类的方法
class Student extends Person{
public Student() {
//调用super构造方法的代码,必须写在子类构造方法的第一行
super("无名称",1);
super.sex = '男';//super的非构造方法可以不在第一行
}
}
1.发生的位置:
重载:一个类中
重写:子父类中
2.参数列表限制:
重载:必须不同
重写:必须相同
3.返回值类型:
重载:与返回值类型无关
重写:返回值类型必须一致
4.访问权限:
重载:与访问权限无关
重写:子类的方法权限必须不小于父类的方法权限
5.异常处理
重载:与异常无关
重写:异常范围可以更小,但是不能抛出新的异常。
final关键字
final用于修饰属性
变量成为了常量,无法对其再次进行赋值
final修饰的局部变量,只能赋值一次(可以先声明否赋值)
final修饰的成员变量,必须在声明时赋值
final用于修饰类
final修饰的类不能被继承
final用于修饰方法
final修饰的方法不能被重写
常量的命名规范:
由多个单词组成,单词与单词之间使用_隔开,所以单词大写
例如:SQL_INSERT
public class Demo1 {
public static void main(String[] args) {
final int a = 10;//后面不能改值
}
}
抽象类必须使用abstract class声明
一个抽象类中可以没有抽象方法。抽象方法必须写在抽象类或者接口中。
格式:
abstract class 类名{ // 抽象类
}
只声明而未实现的方法称为抽象方法(未实现指的是:没有“{}”方法体),抽象方法必须使用abstract关 键字声明。
格式:
abstract class 类名{ // 抽象类
public abstract void 方法名() ; // 抽象方法,只声明而未实现
}
在抽象类的使用中有几个原则:
· 抽象类本身是不能直接进行实例化操作的,即:不能直接使用关键字new完成。
· 一个抽象类必须被子类所继承,被继承的子类(如果不是抽象类)则必须覆写(重写)抽象类中的全 部抽象方法。
1、 抽象类能否使用final声明?
不能,因为final属修饰的类是不能有子类的 , 而抽象类必须有子类才有意义,所以不能。
2、 抽象类能否有构造方法?
能有构造方法,而且子类对象实例化的时候的流程与普通类的继承是一样的,都是要先调用父类中的 构造方法(默认是无参的),之后再调用子类自己的构造方法。
1、抽象类必须用public或procted 修饰(如果为private修饰,那么子类则无法继承,也就无法实现其 抽象方法)。默认缺省为 public
2、抽象类不可以使用new关键字创建对象, 但是在子类创建对象时, 抽象父类也会被JVM实例化。
3、如果一个子类继承抽象类,那么必须实现其所有的抽象方法。如果有未实现的抽象方法,那么子类也必 须定义为 abstract类
如果一个类中的全部方法都是抽象方法,全部属性都是全局常量,那么此时就可以将这个类定义成一个接口。
定义格式:
interface 接口名称{
全局常量 ;
抽象方法 ;
}
格式:
class 子类 implements 父接口1,父接口2…{
}
以上的代码称为接口的实现。那么如果一个类即要实现接口,又要继承抽象类的话,则按照以下的格式编写即可:
class 子类 extends 父类 implements 父接口1,父接口2…{
}
Object类是所有类的父类(基类),如果一个类没有明确的继承某一个具体的类,则将默认继承Object类。
例如我们定义一个类:
public class Person{
}
其实它被使用时 是这样的:
public class Person extends Object{
}
建议重写Object中的toString方法。 此方法的作用:返回对象的字符串表示形式。
Object的toString方法, 返回对象的内存地址
建议重写Object中的equals(Object obj)方法,此方法的作用:指示某个其他对象是否“等于”此对象。
Object的equals方法:实现了对象上最具区别的可能等价关系; 也就是说,对于任何非空引用值x和y ,当且仅当x和y引用同一对象( x == y具有值true )时,此方法返回true 。
equals方法重写时的五个特性:
自反性 :对于任何非空的参考值x ,x.equals(x)应该返回true 。
对称性 :对于任何非空引用值x和y ,x.equals(y)应该返回true当且仅当y.equals(x)回报true 。
传递性 :对于任何非空引用值x , y和z ,如果x.equals(y)回报true个y.equals(z)回报true,然后x.equals(z)应该返回true 。
一致性 :对于任何非空引用值x和y ,多次调用x.equals(y)始终返回true或始终返回false ,前 提是未修改对象上的equals比较中使用的信息。
非空性 :对于任何非空的参考值x , x.equals(null)应该返回false 。
使用包装类还有一个很优秀的地方在于:可以将一个字符串变为指定的基本数据类型,此点一般在接收输入数据上使用较多。
在Integer类中提供了以下的操作方法:
public static int parseInt(String s) :将String变为int型数据
在Float类中提供了以下的操作方法:
public static float parseFloat(String s) :将String变为Float
在Boolean 类中提供了以下操作方法:
public static boolean parseBoolean(String s) :将String变为boolean
多态: 就是对象的多种表现形式,(多种体现形态)
对象的多态性,从概念上非常好理解,在类中有子类和父类之分,子类就是父类的一种形态 ,对象多态性就从此而来。
ps: 方法的重载 和 重写 也是多态的一种, 不过是方法的多态(相同方法名的多种形态)。
重载: 一个类中方法的多态性体现
重写: 子父类中方法的多态性体现。
类似于基本数据类型的转换:
· 向上转型:将子类实例变为父类实例
|- 格式:父类 父类对象 = 子类实例 ;
· 向下转型:将父类实例变为子类实例
|- 格式:子类 子类对象 = (子类)父类实例 ;
试题:设置一个类,命名为MyList
类中包含属性:Object[]element
方法有如下几个:
1.增加方法add:可以向数组属性中依次存储Object,数组内容 存满时,需实现动态扩容(详解在下面)。
2.删除方法remove:可以根据数据或下标,从数组属性中删除 Object数据,删除后,数组后续元素需前移。
3.查询方法get:方法传入下标,返回数组中指定下标的数据。 当前存储数据量size:获取当前存储的有效数据长度
动态扩容详解:无需真正增加原数组的容量,只用将原内容复制到新 的大数组,然后让原数组名称重新等于大数组即可。由于原数组数据在堆中,失去引用会被GC自动回收。
我的实现如下:
public class MyList {
Object[] element = new Object[5];
//当前存储数据量
static int size = 0;
//增加方法add:可以向数组属性中依次存储Object,数组内容 存满时,实现动态扩容。
public void add(Object o) {
element[size] = o;
size++;
//动态扩容
if (element.length == size + 1) {
Object[] test = new Object[size + 6];
for (int i = 0;i < element.length;i++) {
test[i] = element[i];
}
element = test;
System.out.println("扩容完成!总容量更改为:" + element.length);
}
}
//删除方法remove:可以根据下标,从数组属性中删除 Object数据,删除后,数组后续元素需前移。
public void remove(int a) {
if (a > size) {
System.out.println("长度错误!");
return;
}
for (int i = a;i < element.length - 1;i++) {
element[i] = element[i + 1];
}
System.out.println("删除成功!");
size--;
for (int i = 0; i < size; i++) {
System.out.print(element[i] + "\t");
}
System.out.println();
}
//方法重载,删除方法remove:可以根据数据,从数组属性中删除 Object数据,删除后,数组后续元素需前移。
public void remove(Object o) {
int i;
//-1表示没有找到,不然会被赋值为找到的数据的下标
int flat = -1;
for (i = 0;i < size;i++) {
if (this.element[i].equals(o)) {
flat = i;
break;
}
}
if (flat != -1){
remove(i);
}else {
System.out.println("数据不存在!");
}
}
//查询方法get:方法传入下标,返回数组中指定下标的数据。 当前存储数据量size:获取当前存储的有效数据长度
public Object get(int a) {
if (a >= 0 && a < size)
return element[a];
return "下标不正确!";
}
}
测试类:
public class MyListTest {
public static void main(String[] args) {
MyList my = new MyList();
my.add("何世鹏");
my.add("2");
my.add("3");
my.add("4");
my.add("5");
Integer a = new Integer(10);
Integer b = 996;
my.add(b);
my.add("7");
my.add("8");
my.add("9");
my.add(a);
//测试下标超出范围的情况
my.remove(30);
//测试数据错误的情况
my.remove("1024");
//测试找到数据的情况
my.remove("何世鹏");
//测试返回给定下标对应的值
System.out.println(my.get(2));
//下标不正确
System.out.println(my.get(9));
}
}