面向对象和面向过程的区别:面向过程是直接解决问题,而面向对象是将问题模块化,面向对象最大的特征是将生活中的事物进行抽象。
一、面向对象的几大特点:
- 封装性:将内部操作隐藏起来,内部操作对外部而言不可见,其作用是为了进行保护。
- 继承性:在上一辈的基础上继续发展,扩展类的功能
- 多态性:这是一个最重要的环节,利用多态性才可以得到良好的设计。方法的重载,对象的多态性。
OOA(面向对象分析)\OOD(面向对象设计)\OOP(面向对象编程)
二、类与对象
- 类本身是引用数据类型
- 所谓的类值得就是一个共性的概念,而对象指的就是一个具体的可以使用的事物。
- 在实际开发中一定要首先产生类,而后才可以操作对象,对象的所有行为一定在类中进行了完整定义。类中没有定义的功能,那么对象一定是无法使用。类中行为就是方法(即操作的行为)、属性(变量,描述每个对象的特点)
- 定义和使用
如果在程序中进行类的定义要采用如下:
class 类名称{
属性;
属性;
……
方法(){}//方法不是由主类直接调用,而是要通过对象进行调用
}
定义类:类名称要首字母大小,对应的属性要选好自己对应的数据类型,对应的方法要选好返回值类型,每个方法代码量要尽量短。
如果要使用类要用到对象:
- 声明并实例化对象:
类名称 对象名称 = new 类名称()
Person per = new Person();
per.name = "张三";
per.age = 30;
内存划分示意图:
- 分步进行对象实例化:
|-声明对象:类名称 对象名称 默认值为null
|-实例化对象:对象名称 = new 类名称()
Person per = null;
per = new Person();
per.name = "张三";
per.age = 18;
- 对象内存分析:如果要进行对象产生分析,必须要清楚引用类型,引用类型指的是内存空间的操作,会使用两块内存空间:堆内存空间(保存真正的数据,对象的属性信息)和栈内存空间(保存的堆内存的地址,堆内存操作权,保存的是对象名称)。引用数据类型的最大特征在于内存的分配操作,传递的是堆内存的使用权,可以为一个堆内存定义多个栈内存的引用操作。new的作用是开辟内存,内存无法无限开辟,因此需要内存调优。
三、引用传递与垃圾内存回收机制
一块堆内存可以被多个栈内存所指向。
首先来看一个例子:
class Person(){
String name;
int age;
public void tell(){
System.out.println("姓名:"+name+",年龄:"+age);
}
}
public class Demo{
public static void main(String args[]){
Person per1 = null;
Person per2 = null;
per1 = new Person();
per2 = ne Person();
per1.name = "张三";
per1.age = 30;
per2.name = "李四";
per2.age = 33;
per2 = per1;//将per1的堆内存空间使用权给per2,此时生成了一个垃圾内存
System.out.print("per1对象中的内容");
per1.tell();
System.out.print("per2对象中的内容");
per2.tell();
}
}
结果:
内存分析:
在程序开发过程中,所谓的垃圾空间将不定期被java的垃圾收集器GC进行回收实现内存空间的释放。但是gc会造成程序性能的下降,因此要控制好对象的产生数量,不用的对象不要产生。new对象太多就会产生性能下降。
四、private实现封装
封装就是保护内容,保证某些属性或方法可以不被看见。
封装的基本语法:
为属性封装:private 属性类型 属性名称
为方法封装:private 方法返回值 方法名称(参数列表){}
考虑没有封装会怎么样?
public class Demo{
public static void main(String args[]){
Person per = new Person();
per.name = "张三";
per.age = -200;
}
}
从语法上看,年龄设置为-200没有任何错误。但是逻辑上没有人的年龄是-200,要想回避此问题,就要考虑让类的外部不直接对对象进行操作。这里解释一下为什么说是外部操作,因为我们在开发中main()函数就相当于客户端控制台,因此我们把main函数里能进行操作的称为外部操作。
在属性声明前加上private定义,如果其他类直接使用属性就就会出现错误,因此引入setter和getter。setter用于对属性的设置,getter用于对属性的取得。类的使用原则,类中的所有属性必须使用private封装,private封装需要被外部使用的就必须使用getter和setter。
格式:
private String name;
setter设置: public void setName(String n){name = n ;}
getter取得: public String getName(){return name;}
再来看一段代码:
public class Demo {
public static void main(String[] args) {
Person per1 = new Person();
per1.setName("张三");
per1.setAge(-30);
per1.tell();
}
}
class Person{
private String name;
private int age;
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 void tell() {
System.out.println("姓名:"+name+"\n年龄:"+age);
}
}
我们发现年龄的设定还是负数,这是不符合要求的,因此我们要加上一个判断,这个判断我们要写在setter方法里。外部将值传入时进行一个拦截判断。
public void setAge(int age) {
if(age>=0 && age<100){
this.age = age;
}
}
输出:
姓名:张三
年龄:0 //这里是因为int的默认值是0
五、构造方法和匿名对象
实例化对象的产生格式:
类名称 对象名称 = new 类名称()
类名称,没有类就不知道对象的属性。对象名称,唯一的标记。
new :开辟内存空间 只要一有对象实例化则会调用构造方法。
类名称():构造方法
构造方法:所谓的构造方法就是使用new来实例化对象的时候方法名称。必须要遵守以下原则,构造方法与类名称相同,且没有返回值类型声明,在类中不写构造方法的定义程序会自动定义一个无参构造方法。如果类中有一个有参构造,那么无参构造就不会自动生成,无参构造其实就是为了使类中的属性实例化。构造方法的调用和分配几乎同步完成,可以利用构造方法对类中的属性进行初始化,通过构造方法设置内容实际上可以避免重复的setter调用。在实际开发中,setter可以进行对属性进行修改。构造方法可以进行重载。
构造方法的定义格式:
class 类名称{
访问权限 类名称 (类型1 参数1, 类型2 参数2,......){
程序语句:
.......//构造方法没有返回值,切不能使用return返回一个值
}
}
既然构造方法没有返回数据,为什么不用void?
类中包含:属性,普通方法,构造方法
|- 属性是对象开辟堆内存的时候开辟的空间
|- 普通方法在对象实例化完成时在调用的,可以调用多次 public void Person()
|- 构造方法是在使用关键字new同时调用 public Person()。针对这句话使用如下例子:
public class Demo {
public static void main(String[] args) {
System.out.println("声明对象:Person per = null");
Person per = null;
System.out.println("实例化对象:per = new Person()");
per = new Person();
}
}
class Person{
public Person() {
System.out.println("产生一个新的person对象");
}
}
输出结果:
声明对象:Person per = null
实例化对象:per = new Person()
产生一个新的person对象
每个类中肯定都会有一个构造方法,如果一个类中没有申明一个明确的构造方法则会自动生成一个无参的什么都不做的构造方法 。构造方法主要是为了类中的属性初始化,既然是方法,则方法中肯定可以传递参数,此时定义一个构造,同时向里面传递参数。其实这时候构造函数就相当于一个普通的无返回值类型 的方法。
class Person{
private String name;
private int age;
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 Person(String n , int a) {
this.setName(n);
this.setAge(a);
}
public void tell() {
System.out.println("姓名:"+this.getName()+"年龄:"+this.getAge());
}
}
public class Demo{
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 String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
if(age>0&&age<100) {
this.age = age;
}
}
public Person(String n , int a) {
this.setName(n);
this.setAge(a);
}
public void tell() {
System.out.println("姓名:"+this.getName()+"\n年龄:"+this.getAge());
}
}
public class Demo{
public static void main(String args[]) {
new Person("张三",30).tell();//匿名对象,只需要new类名称(设置属性)+方法
}
}
七、简单Java类
任务:定义一个雇员类,该类中包含雇员编号、姓名、职位、基本工资、佣金几个基本属性,这种类叫做简单Java类。
注意:类的名称要有意义,可以描述一类事物,类中所有的属性必须有private封装,且必须按照要求提供setter、getter方法,类中要有一个无参构造方法。所有方法中不能有System.out.println(),类中要有一个返回完整信息的方法。
Class Emp{
private int empno;
private String ename;
private String ejob;
private double esal;
private double ecom;
}
public Emp(){}
public Emp(int empno; String ename; String ejob;double esal;double ecom;){
setEmpno(eno);
setEmpno(eno);
setEjob(ej);
setEsal(sa);
setEcom(ec);
}
public void setEmpno(int eno){
empno = eno;
}
public void setEmpno(int eno)){
ename = ena;
}
public void setEjob(String ej){
ejob = ej;
}
public void setEsal(double sa){
esal = sa;
}
public void setEcom(double ec){
ecom = ec;
}
public int getEmpno(){
return empno;
}
public String getEname(){
return ename;
}
public String getEjob(){
return ejob;
}
public double getEsal(){
return esal;
}
public double getEcom(){
return ecom;
}
public String getInfo(){
return "empno="+empno+"\n"+
"ename ="+ename +"\n"+
"ejob ="+ejob +"\n"+
"esal ="+esal +"\n"+
"ecom ="+ecom +"\n"+
}
public class Testdemo(){
public static void main(String args[]){
System.out.println(new Emp(7369,"Smith","Coder",800.0,0.0).getInfo());
}
}
八、this关键字
this的作用:
- 表示类中的属性
- 可以使用能this调用本类的构造方法
- this表示当前对象
1.this表示类中的属性
public class Demo{
public static void main(String args[]) {
Person per1 = new Person("张三",30);
System.out.println(per1.getInfo());
}
}
class Person{
private String name;
private int age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;//这里this.name指代的是属性中的那个name
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;//这里的this.age指代的是属性中的那个age
}
public Person(String name,int age){
this.name = name;
this.age = age;
}
public String getInfo() {
return "姓名:"+name+"\n年龄:"+age;
}
}
2.使用this调用本类的构造方法
如果当前程序中有多个构造方法,可以利用this关键字互相调用
public class Demo{
public static void main(String args[]) {
Person per1 = new Person("张三",30);
System.out.println(per1.getInfo());
}
}
class Person{
private String name;
private int age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;//这里this.name指代的是属性中的那个name
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;//这里的this.age指代的是属性中的那个age
}
public Person(){//无参构造
System.out.println("新对象实例化");
}
public Person(String name){//传一个参数
this();//调用本类中的无参构造方法
this.name = name;
}
public Person(String name,int age)//传递两个参数
this(name);//调用本类中的有相同参数的构造方法
this.age = age;
}
public String getInfo() {
return "姓名:"+name+"\n年龄:"+age;
}
}
this调用本类中的构造方法应该遵循的原则:
- this()调用构造方法的语句只能放在构造方法的首行
- 在使用this调用本类中的其他构造方法时,至少有一个构造方法是不用this调用的
3.使用this表示当前对象,当前正在操作本方法的对象称为当前对象
public class Demo{
public static void main(String args[]) {
Person per1 = new Person();
Person per2 = new Person();
System.out.println("Main()方法"+per1);
per1.getInfo();
System.out.println("Main()方法"+per2);
per2.getInfo();
}
}
class Person{
public String getInfo() {
System.out.println("Person类"+this);
return null;
}
}
九.static关键字
每一个对象都拥有各自的堆栈空间,堆内存空间保存每个对象的各自属性,但是所有的static属性是保存在全局数据区中,所有的对象指向全局数据区中的一个内容,所有当一个对象修改了static属性内容,所有的对象static属性内容将全部变化。
Java中的内存区域:
- 栈内存:可以保存对象的名称(保存,访问的堆内存的地址)
- 堆内存:保存每个对象的具体属性
- 全局数据区:保存static类型的属性
- 全局代码区:保存所有方法的定义
static可以用来描述属性和方法,如果希望一个属性被所有对象所共同拥有,则可以将其声明为static类型。声明static类型的属性或方法之后,此属性或方法也称为类方法,可以由类名称直接调用。格式:
类名称.属性 = "";
如果对一个方法使用static,则此方法可以直接使用类名称进行调用,使用static方法不能调用非static的属性或方法。static属性和方法可以在对象没有实例化的时候就直接进行调用。
例题:使用static为对象进行自动编名操作:
public class Demo{
public static void main(String args[]) {
System.out.println(new StaticDemo().getName());
System.out.println(new StaticDemo("zhang").getName());
System.out.println(new StaticDemo().getName());
System.out.println(new StaticDemo("LLLL").getName());
System.out.println(new StaticDemo().getName());
}
}
class StaticDemo{
private String name;
private static int count = 0;//所有对象共享此属性
public StaticDemo() {
count++;//有对象就自增
this.name = "Demo_"+count;//自动进行编写名字操作
}
public StaticDemo(String name) {
this.name = name;//可以通过构造赋值
}
public String getName() {
return this.name;
}
}
十.main方法解读
public static void main(String args[]){}
public:表示此方法可以被外部所调用
static:表示此方法可以由类名称直接调用
void:主方法是程序的起点,所以不需要任何返回值
main:系统规定好的默认调用方法名称
String args[]:表示的是运行时的参数,参数传递的形式:Java类名称,参数1,参数2,参数3...
十一、内部类
在一个类A的内部还存在着另一个类B,则B称为内部类,A称为外部类。内部类可以声明为public和private,其访问限制与成员变量和成员方法完全相同。
内部类的定义格式:
标识符 class 外部类的名称{
//外部类的成员
标识符class内部类的名称{
//内部类的成员
}
}