java知识2-----核心1-面向对象基础

面向对象、类与对象

面向对象的基本概念

程序的发展主要经历了,面向过程和面向对象编程。

  • 二者的差异是,面向对象在动手之前对整体有一个设计和分析的过程,需要的东西都提前做准备和设计。面向过程则是直接动手做,过程中需要什么工具随取随用。

这在制作过程中,如果想要修改,那么面向对象会非常清晰,非常好修改,但是面向过程的话,就会不是特别好修改。

  • 面向对象对现实世界有一个抽象的过程,相比面向过程多了一些分析方法,设计和分析过程。

C语言就是面向过程的,C++、java就是面向对象的。

面向对象的3个主要特征

  1. 封装 encapsulate
    -对外部不可见
    封装性:封装性就是对外部不可见,起到保护程序的某些内容,但是不能封装的死死的,得向外提供接口。
  2. 继承 inheritance
    -扩展类的功能
    继承性:扩展类的功能。
  3. 多态 polymorphism
    -方法重载
    -对象多态
    多态性:方法名相同,但是方法的参数类型或者个数不同就是方法的重载,是一种多态。还有对象的多态性。
    3大特性都是为了在程序设计中达到解耦合的目的。

类与对象的关系、类的定义、对象的声明、创建及使用

整个面向对象中,最重要的是类与对象,因为面向对象的核心组成就是类与对象。

什么是类?什么是对象?

类对某一类事物的描述,是从具体对象中抽象出来的,是抽象的,是概念上的定义。
对象是实际存在的该类对象的具体的一个个体。也称为实例(instance)。
类相当于是一个模板,依照模板产生具体的产品。

使用关键字class即可定义一个类。
一个类定义之后不能直接使用,需要产生对象。

类名 对象名称 = null;
对象名称 = new 类名();
类名 对象名称 = new 类名();

java中的内存划分、引用传递、垃圾产生

类和数组一样属于引用数据类型,引用数据类型肯定存在栈内存到堆内存的引用关系。实际上在类与对象的关系中,也存在这样的引用关系。

Person per = new Person();

声明对象: Person per=null; Person per表示声明对象,对象的声明是在栈内存中声明的,与数组一样数组名称是保存在栈内存之中。
只开辟了栈内存的对象是无法使用的,必须有其堆内存的引用才可以使用。
实例化对象: new Person();在堆中开辟空间,对象实例化后,其所有的属性的内容都是默认值。

对象保存在栈内存之中,
属性保存在堆内存之中,
方法保存在方法区,该区域是所有对象共享的。

注意点:
1,在使用对象的时候,对象必须被实例化之后才可以使用(实例化对象,并不单单指直接使用new关键字实现,只要其有堆内存的空间指向,则就表示实例化成功)。
2,如果不实例化,直接使用对象,会报NullPointerException空指针的空指向异常。
3,在引用操作中,如果一个对象没有堆内存的引用,而调用了类中的属性或方法,就会出现该问题。
4,所谓引用传递,实际上传递的就是堆内存的使用权,可以为一个堆内存空间定义多个栈内存的引用操作。

class Person{
	String name ;		// 声明姓名属性
	int age ;			// 声明年龄属性
	public void tell(){	// 取得信息
		System.out.println("姓名:" + name + ",年龄:" + age) ;
	}
};
public class ClassDemo06{
	public static void main(String args[]){
		Person per1 = null ;		// 声明per1对象
		Person per2 = null ;		// 声明per2对象
		per1 = new Person() ;		// 实例化per1对象
		per2 = new Person() ;		// 实例化per2对象

		per1.name = "张三" ;		// 设置per1中的name属性内容
		per1.age = 30 ;				// 设置per1中的age属性内容
		per2.name = "李四" ;
		per2.age = 33 ;				// 设置per2中的age属性内容

		per2 = per1 ;				// 把per1的堆内存空间使用权给per2
		System.out.print("per1对象中的内容 --> ") ;
		per1.tell() ;				// 调用类中的方法
		System.out.print("per2对象中的内容 --> ") ;
		per2.tell() ;				// 调用类中的方法
	}
};

因为per2本身有堆内存的空间指向,所以如果要想再指向per1对应的空间,则必须先断开已有的链接。
per2=per1;之后,per2原本的空间指向的堆内存没有了任何栈内存空间所引用了,就成了垃圾空间,等待垃圾回收机制进行回收。
因为per2改变了指向,所以其原本的堆内存空间就没有了任何栈的引用,则这样的空间就称为垃圾,等待垃圾回收机制回收。
垃圾回收机制简称GC。
总结:
1,栈与堆内存的关系;
2,对象保存在栈内存之中,而具体的内容保存在堆内存之中。
3,对象的引用传递,实际上传递的就是堆内存空间的使用权。
4,垃圾的产生。

内存操作:为属性赋值-----封装性

封装产生的目的

封装就是保护内容。
保证某些属性、方法不被外部看见。
看一下代码:

class Person{
	String name ;			// 声明姓名属性
	int age ;				// 声明年龄属性
	public void tell(){
		System.out.println("姓名:" + name + ",年龄:" + age) ;
	}
};
public class EncDemo01{
	public static void main(String arsgh[]){
		Person per = new Person() ;	// 声明并实例化对象
		per.name = "张三" ;			// 为name属性赋值
		per.age = -30 ;				// 为age属性赋值
		per.tell() ;
	}
};

从语法角度看,没有任何问题,但是从实际角度看,年龄是个负数,明显不合理,那么以上的代码如何修改最合理?对年龄的验证放在哪里最合理,是对象的定义的Person类中还是对象的使用的类EncDemo01中?这是个好问题!!!
首次改进代码:—属性封装起来。java中使用private关键字实现属性、方法的封装。
如下:

class Person{
	private String name ;			// 声明姓名属性
	private int age ;				// 声明年龄属性
	public void tell(){
		System.out.println("姓名:" + name + ",年龄:" + age) ;
	}
};
public class EncDemo02{
	public static void main(String arsgh[]){
		Person per = new Person() ;	// 声明并实例化对象
		per.name = "张三" ;			// 为name属性赋值
		per.age = -30 ;				// 为age属性赋值
		per.tell() ;
	}
};

运行程序发现,属性封装的死死的了,外部无法调用到了,继续优化代码。
二次优化:----给封装的死死的属性向外提供访问接口,setter、getter方法。
如下:

class Person{
	private String name ;			// 声明姓名属性
	private int age ;				// 声明年龄属性
	public void setName(String n){	// 设置姓名
		name = n ;
	}
	public void setAge(int a){		// 设置年龄
		age = a ;
	}
	public String getName(){		// 取得姓名
		return name ;
	}
	public int getAge(){			// 取得年龄
		return age ;
	}
	public void tell(){
		System.out.println("姓名:" + name + ",年龄:" + age) ;
	}
};
public class EncDemo03{
	public static void main(String arsgh[]){
		Person per = new Person() ;	// 声明并实例化对象
		per.setName("张三") ;		// 调用setter设置姓名
		per.setAge(-30) ;			// 调用setter设置年龄
		per.tell() ;				// 输出信息
	}
};

OK,至此也做了封装了,语法不报错了,

仔细想想在哪里做对年龄合法化的判断,其实应该加载类本身,如果年龄不合理则就不应该为属性赋值,类本身就应该就给提供良好的对象,而不是对象生成以后再在外面给对象做判断和限制。

所以,属性的合理性控制的判断,需要加载在Person类的定义中,并且是在向外提供的setter方法中。
三次优化:------setter中增加合理性判断。

class Person{
	private String name ;			// 声明姓名属性
	private int age ;				// 声明年龄属性
	public void setName(String n){	// 设置姓名
		name = n ;
	}
	public void setAge(int a){		// 设置年龄
		if(a>=0&&a<=150){			// 加入验证
			age = a ;
		}
	}
	public String getName(){		// 取得姓名
		return name ;
	}
	public int getAge(){			// 取得年龄
		return age ;
	}
	public void tell(){
		System.out.println("姓名:" + name + ",年龄:" + age) ;
	}
};
public class EncDemo04{
	public static void main(String arsgh[]){
		Person per = new Person() ;	// 声明并实例化对象
		per.setName("张三") ;		// 调用setter设置姓名
		per.setAge(-30) ;			// 调用setter设置年龄
		per.tell() ;				// 输出信息
	}
};

tell 方法中调用了属性,为了调用对象明确起见,我们最好给属性明确指出调用的是本类对象的属性,所以需要加上this关键字。this关键字表示当前对象。

封装的实现

1,private关键字。
2,setter、getter。

访问封装的内容

setter、getter方法的定义

构造方法、匿名对象

构造方法的概念以及调用时机

对象的产生格式:

类名称 对象名称 = new 类名称();

这里的类名称()就是构造方法。一有对象产生,则就会调用构造方法。声明对象并不会调用构造方法,只有实例化对象的时候才回去调用构造方法。

class Person{
	public Person(){		// 声明构造方法
		System.out.println("一个新的Person对象产生。") ;
	}
};
public class ConsDemo01{
	public static void main(String args[]){
		System.out.println("声明对象:Person per = null ;") ;
		Person per = null ;	// 声明对象时并不去调用构造方法
		System.out.println("实例化对象:per = new Person() ;") ;
		per = new Person() ;//实例化对象
	}
};
1,构造方法的名称必须与类名称一致。
2,构造方法的声明处不能有任何返回值类型的声明。
3,不能在构造方法中使用return返回一个值。

问题:写一个类,没有明确的去定义一个构造方法,这个类为什么还是可以使用呢?
因为java的操作机制中,如果一个类中如果没有明确的声明一个构造方法,则会自动生成一个无参的什么都不做的构造方法供使用。保证类中至少有一个构造方法。

1,每个类中肯定都会至少有一个构造方法。
2,如果一个类中没有声明一个明确的构造方法,则会自动生成一个无参的什么都不做的构造方法。
3,但是,一旦明确的声明了一个构造方法,就不会自动生成这个无参的构造方法了。
class Person{
	public Person(){} // 如果没有编写构造方法,则会自动生成此代码
}

构造方法的作用是为类中的属性初始化。

class Person{
	private String name ;
	private int age ;
	public Person(String n,int a){		// 声明构造方法,为类中的属性初始化
		this.setName(n) ;
		this.setAge(a) ;
	}
	public void setName(String n){
		name = n ;
	}
	public void setAge(int a){
		if(a>0&&a<150){
			age = a ;
		}
	}
	public String getName(){
		return name ;
	}
	public int getAge(){
		return age ;
	}
	public void tell(){
		System.out.println("姓名:" + this.getName() + ";年龄:" + this.getAge()) ;
	}
};
public class ConsDemo02{
	public static void main(String args[]){
		System.out.println("声明对象:Person per = null ;") ;
		Person per = null ;	// 声明对象时并不去调用构造方法
		System.out.println("实例化对象:per = new Person() ;") ;
		per = new Person("张三",30) ;//实例化对象
		per.tell() ;
	}
};

根据以上代码,再次强调:构造方法的作用是为类中的属性初始化。

构造方法重载

构造方法与普通方法一样是支持重载操作的。只要方法的类型或者参数个数不同,则就可以完成重载操作。

class Person{
	private String name ;
	private int age ;
	public Person(){}					// 声明一个无参的构造方法
	public Person(String n){			// 声明有一个参数的构造方法
		this.setName(n) ;
	}
	public Person(String n,int a){		// 声明构造方法,为类中的属性初始化
		this.setName(n) ;
		this.setAge(a) ;
	}
	public void setName(String n){
		name = n ;
	}
	public void setAge(int a){
		if(a>0&&a<150){
			age = a ;
		}
	}
	public String getName(){
		return name ;
	}
	public int getAge(){
		return age ;
	}
	public void tell(){
		System.out.println("姓名:" + this.getName() + ";年龄:" + this.getAge()) ;
	}
};
public class ConsDemo03{
	public static void main(String args[]){
		System.out.println("声明对象:Person per = null ;") ;
		Person per = null ;	// 声明对象时并不去调用构造方法
		System.out.println("实例化对象:per = new Person() ;") ;
		per = new Person("张三",30) ;//实例化对象
		per.tell() ;
	}
};

匿名对象

匿名对象,就是没有名字的对象,在java中,如果一个对象只使用一次,则就可以将其定义成匿名对象。

class Person{
	private String name ;
	private int age ;
	public Person(String n,int a){		// 声明构造方法,为类中的属性初始化
		this.setName(n) ;
		this.setAge(a) ;
	}
	public void setName(String n){
		name = n ;
	}
	public void setAge(int a){
		if(a>0&&a<150){
			age = a ;
		}
	}
	public String getName(){
		return name ;
	}
	public int getAge(){
		return age ;
	}
	public void tell(){
		System.out.println("姓名:" + this.getName() + ";年龄:" + this.getAge()) ;
	}
};
public class NonameDemo01{
	public static void main(String args[]){
		new Person("张三",30).tell() ; //这个new出来的对象就是匿名对象
	}
};

使用了关键字new就表示开辟了堆内存空间,只有开辟了堆内存空间的对象才有意义。这个对象可以没有栈内存对象的指向,直接可以调用方法。这就是匿名对象。

所谓的匿名对象,就是比普通的对象少了一个栈内存的引用关系。

总结:
1,对象在实例化时,必须调用构造方法,每个类都有至少一个构造方法。
2,匿名对象是只开辟了堆内存的实例对象。

String类及其常用方法

String类的两种实例化方式及其区别

A:直接赋值
public class StringDemo01{
	public static void main(String args[]){
		String name = "jake" ;			// 实例化String对象
		System.out.println("姓名:" + name) ;
	}
};
B:通过关键字new
public class StringDemo02{
	public static void main(String args[]){
		String name = new String("jake") ;			// 实例化String对象
		System.out.println("姓名:" + name) ;
	}
};
两种实例化方式的区别、字符串的特征

两种实例化方式,使用哪种更合适?
要想解决这样的问题,则首先必须从字符串的特征说起。
字符串什么特征?:
字符串特征1:一个字符串就是一个String的匿名对象。
以下代码可以正确运行,就证明了,一个字符串就是一个String的匿名对象。

public class StringDemo06{
	public static void main(String args[]){
		System.out.println("hello".equals("hello")) ;
	}
};

分析:

String name="jake"

对于这样的代码,就表示将一个堆内存空间的指向给了栈内存空间。

明确这些之后,再来分析,String的实例化使用哪种方式更合适的问题:
看如下代码,分析直接赋值时候的内存使用:

public class StringDemo07{
	public static void main(String args[]){
		String str1 = "hello" ;					// 直接赋值
		String str2 = "hello" ;					// 直接赋值
		String str3 = "hello" ;					// 直接赋值
		System.out.println("str1 == str2 --> " + (str1==str2)) ;	// true
		System.out.println("str1 == str3 --> " + (str1==str3)) ;	// true
		System.out.println("str2 == str3 --> " + (str2==str3)) ;	// true
	}
};

“==”比较的是地址,三个对象比较结果都是true,也就是三个内存地址是相同的,是同一个对象,三个栈内存引用指向的是同一个堆内存空间地址。
在这里插入图片描述
当创建相同的对象,java机制会去常量池去查找是否有创建过,如果已经创建过就不再重新创建,直接指向已有的地址,如果找不到才会去重新创建。这样可以有效的节省内存。

再分析使用new赋值时候的内存使用:

public class StringDemo08{
	public static void main(String args[]){
		String str1 = new String("hello") ;	
	}
};

在这里插入图片描述
使用了new关键字,肯定开辟了堆内存,实际使用的也是new开辟的空间里的对象,但是双引号里的值又是String的一个匿名对象,这个匿名对象是在常量池的,这个对象并不会被用到,就会被jc回收。
所以,至此得出:
使用直接赋值的方式只需要一个实例化对象即可,而使用new String的方式则意味着开辟了两个内存对象。
所以开发中,最好使用直接赋值的方式进行赋值。

字符串特征2:字符串的内容一经声明不可改变。

public class StringDemo09{
	public static void main(String args[]){
		String str = "hello" ;		// 声明字符串
		str = str + " world!!!"	;	// 修改字符串
		System.out.println("str = " + str) ;
	}
};

此时,字符串的对象是改变了,但是字符串变了么?
分析一下:
首先,之前已经得出,一个双引号里的字符串,就是String的一个匿名对象。
分析内存:
在这里插入图片描述
实际上字符串内容的改变,改变的是内存地址的引用关系。原本的字符串并没有并改变。
所以开发中String尽量需要避免如下的使用:

public class StringDemo10{
	public static void main(String args[]){
		String str1 = "LiXingHua" ;		// 声明字符串对象
		for(int i=0;i<100;i++){			// 循环修改内容
			str1 += i ;					// 字符串的引用不断改变
		}
		System.out.println(str1) ;
	}
};

这样的代码,没有语法问题,但是从内存角度分析,堆栈指向连接需要断开连接100次,这样的操作性能很低,应该避免使用。但是如果就是有这样的使用场景,需要在之前基础上改变字符串内容,则可以使用功能java常用类库中的StringBuilderStringBuffer来完成。

String的两种比较操作

String的赋值有两种,比较也有两种。

使用 “==”进行String内存地址比较

对于基本数据类型来说,“ == ”比较的是值,但是对于String这种引用数据类型来说,“ == ” 比较的是内存地址。

public class StringDemo03{
	public static void main(String args[]){
		int x = 30 ;
		int y = 30 ;
		System.out.println("两个数字的比较结果:" + (x==y)) ;
	}
};
public class StringDemo04{
	public static void main(String args[]){
		String str1 = "hello" ;					// 直接赋值
		String str2 = new String("hello") ;		// 通过new赋值
		String str3 = str2 ;					// 传递引用
		System.out.println("str1 == str2 --> " + (str1==str2)) ;	// false
		System.out.println("str1 == str3 --> " + (str1==str3)) ;	// false
		System.out.println("str2 == str3 --> " + (str2==str3)) ;	// true
	}
};

在这里插入图片描述

使用 equals() 进行值比较

String引用数据类型使用String类提供的 equals() 比较的是值。

public class StringDemo05{
	public static void main(String args[]){
		String str1 = "hello" ;					// 直接赋值
		String str2 = new String("hello") ;		// 通过new赋值
		String str3 = str2 ;					// 传递引用
		System.out.println("str1 equals str2 --> " + (str1.equals(str2))) ;	// true
		System.out.println("str1 equals str3 --> " + (str1.equals(str3))) ;	// true
		System.out.println("str2 equals str3 --> " + (str2.equals(str3))) ;	// true
	}
};
总结

1,String要使用直接赋值的方式,因为使用new会开辟两个空间,造成内存的浪费。
2,一个字符串就是String的一个匿名对象。
3,字符串的比较有两种方式,“== ”和equals()。

String类的常用方法

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值