java学习笔记12

1、包(package)

 包的作用就是对类文件进行分类管理,避免同一目录下出现同名但不同内容的类文件的无法同时存在(先生成的被后生成的覆盖掉,只有一个能存在)的现象出现。包也是一种封装方式。

注意:一旦对类进行打包,类就相当于有了所属,在运行时必须在类名前带上包名才可以使用,包括在别的包中new对象,或者运行某个class文件。(注意在编译时,可直接像以前那样编译类名就ok,但是对应的class文件会生成到指定的文件夹中。所以运行的时候必须带上包的名字,在别的包中的类中new自己包对象是,因为要加载自己类的class文件,所以在new对象的时候在类前也必须带上类文件所属的包名字)。

文件夹在java中就体现为“包“,”包“在windows中就体现为文件夹。所以在java代码中指定了包后,要在windows的对应地方新建一个和包名一致的文件夹。



但是每次都要手动生成一个文件夹,再把class文件扔到文件夹中,是比较麻烦的。所以javac时,直接在指定的目录下生成我们的class文件。javac - d . PackageDemo.java直接就在当前目录下生成一个文件夹(包)名字是mypack。



2、不同包之间的访问

要想在不同的包之间访问彼此的类,那么那个类就必须是public权限的,如果类的权限不是public,那么这个类就仅限于同一个包里的其他类可以使用。而一个类写成public权限后,其类名必须和java文件名一致才可以。

这里还要引入一个新的权限关键词 protected,被protected修饰的东西,不同于public权限,是不能直接访问的,而必须是其子类才可以进行访问。如果没有被protected修饰(没有什么权限关键字修饰也即是默认权限),那么就算是其子类也无法访问。

下面总结下四种权限的范围的总结:


可以看到,private的权限是最低的。被protected修饰的只能别的包中继承了自己的类才能使用。

首先:我们在C盘生成了packa的包(不管你是手动新建一个名为packa的文件夹之后再把编译得到的class文件扔进文件夹中,还是利用指令javac -d . DemoA.java来建立class文件自动在packa文件夹中),之后javac编译PackageDemo.java,报错:

现在情况是:已经在C盘的myclass文件夹下生成了packa文件夹(里面有DemoA的class文件),我想在myclass文件夹下生成mypack文件夹(里面应该有PackageDemo的class文件),但是这一步编译失败。





错误原因及更改方法如下:

错误原因及更改方法如下:

更改之后,重新编译javac -d c:\myclass PackageDemo.java仍然报错。新的错误如下:







在更正了类名后,又出现了新的错误,是因为编译PackageDemo时,要在当前目录下找DemoA.class文件,但是当前目录是D盘,而我们生成的DemoA.class所属的packa的文件夹在C盘的myclass文件夹下,编译时的搜索路径默认是在当前目录下(E盘),所以找不到软件包packa,故修改办法是重设classpath = c:\my class (指向包所在的父目录),这样PackageDemo在编译时才能在路径指示下找到packa包。更改路径后又出现了新的问题:



此时出现的问题是权限问题:

在一个包里定义的类,想要在包意外被访问,那么这个类必须要有足够大的权限:public权限,类里面的方法要想在包外别的类中被访问也必须具有足够的权限:public权限。所以定义了包的类如果没有加public权限,相当于被封装了,只能被同一个包内的别的类访问,不能被包外的别的类所访问。

如果没有对DemoA中的方法提升到public权限,那么想在包mypack文件夹中的PackageDemoclass中调用show()方法就会报错,更改方法:方法改成public权限。


总结如下:


下面再讨论下包与包之间的类能不能继承:

先新建一个packb包的DemoB


之后让packa包的DemoA继承DemoB,注意DemoB是所属与包packb的类,故在继承时要写成packb.DemoB,这和继承时直接写成继承DemoB是完全不同的(写成DemoB会报错)!!!





注意之前已把classpath改为C盘的myclass,所以可以在E盘下直接java运行mypack.PackageDemo.class文件


packa包中的DemoA继承packb包中的DemoB之后,可以调用DemoB中的method()方法没有问题,但是mypack包中的PackageDemo不是DemoB的子类,仍然可以直接创建DemoB对象并调用packb包中DemoB的method方法,那A对B的爹就白喊了,此时关键字protected的作用就体现出来了,packb包中DemoB的method方法被protected修饰后,只有DemoB的子类才可以访问method()方法,其他类不能通过直接new一个DemoB的对象,就以DemoB.method()的形式来调用method()方法。

这和以前定义private类似,在一个类(譬如A类)中定义成员变量num为private后,不能在别的类(譬如B类)中new出一个A类对象,通过A.num的形式访问num。但是protected的权限比private权限高些,因为被private权限修饰的成员就算是子类也不能访问。


包和包之间只有public和protected可以用,而且protected只能用于(包与包之间的)子类与父类

3、嫌上面的麻烦,就可以使用关键字import来导入包,在被导入后就不同包里的类效果就可以变成在同一个包中(同一个文件夹下)一样。


还费事就可以用通配符*,通通匹配的符号packa.*

import 相当于就是为例简化类名书写。

4、jar是java的一个压缩工具,通过dos操作指令就可以实现把生成的包再压缩成一个jar文件,当然还可以通过指令把jar压缩包再解压成普通的包(压缩包)。使用jar包的好处在于可以先把所要用的包压缩成jar压缩包,在直接把classpath路径设置为jar包,这样就可以直接运行,不需要解压jar包,不需要再程序中写import,或者把类名写的好长。



5、这里放几道练习题





上图中如果只是单纯的覆盖add()方法会报错,因为add(int x,int y)中的x和y都是局部变量,生命周期短,用成员变量来保存两个变量,则可以在show()方法中继续使用。


上图中,因为指定了有参数的父类构造函数,所以不再提供默认的无参构造函数super(),子类构造函数中自然就无法运行super()。


子类也有get(),父类也有get(),两者只是返回值类型的不同,这样在调用时会不确定调用哪个,所以编译失败。







父类异常在写在前面是会报错的,一般父类异常都写在最后。算是语法规定吧。

 

上图中main()函数为静态,所以不能直接调用非静态的show()方法,所以必须new一个对象,通过对象来调用show()方法,接着在show()方法的参数地方来new一个匿名内部类,而且要注意func()的权限一定是public,因为接口类里的方法的权限都是public的,只是有时省去没写,但是在覆盖重写时一定要重新写出public权限。


注意output是静态的成员变量,一直存在,所以一直是对它进行修改,而且是字符串相加,而不是简单的数值相加。


7、注意:再次强调return的作用,return结束的是return语句所在的当前函数,如果这个return在主函数中,碰见return就把主函数结束掉了。

例如下图:



8、使用异常有什么好处?

在学习异常之前,我们都是通过if判断语句来对传入的不合理参数进行处理,例如下图


当传入的参数为非法的负数时,if里面判断,并且return结束了,构造函数,但是主函数的最后一句还是会执行,打印出a的默认值0,但是因为传入的负数参数都已经非法,再显示出面积值为0也是不合适的,所以最合理的方式是在if判断语句里面写成抛出异常,只有这样,下面的语句才会全都不执行。因为传入的参数为负数是比较致命,没办法(必要)内部修改,所以这个异常继承的是RuntimeException。



1、什么是进程

进程:正在进行中的程序(直译),一个程序一旦运行就在内存中开辟空间,那个空间就是进程,也即进程对应程序在内存中开辟的空间。进程只是在内存中分配程序执行的内部空间,并不执行,而线程是控制程序执行路径,是负责进程中内容执行的一个控制单元。线程就是代码执行路径,搞一个线程也就是为了去执行一块代码。多线程的例子:360一边体检,一边清理垃圾。多线程就是为了让多块代码同时执行。每一个线程都有自己运行的内容。这个内容可以称为线程要执行的任务。


cpu在不同程序包括程序里的多个线程之间随机切换,各自执行一会儿,具体怎样随机由时间片来确定。

3、java虚拟机中就有多线程,譬如:一个(主)线程来执行main()函数,一个线程来回收垃圾等等,彼此之间是相互不耽误的。

垃圾回收器启动之后并不一定立即开始回收堆里面的垃圾对象,有时候可能没回收完就因为java虚拟机的技术而强制停掉了。注意:主线程结束了,但是虚拟机还有别的线程可能没执行完,所以虚拟机还没有立即退出。


3、线程有自己的名字,Thread-编号(从0开始)。通过getName()可以得到当前线程(已知道的)对象,而Thread.currentThread()得到当前正在运行的线程的引用,故Thread.currentThread().getName()就得到了当前正在运行的线程的名字。 





Thread类中自带给线程起名字的方法Thread(String name),而Demo类是继承自Thread类,所以Demo()构造方法在第一句是super( ),只需将super()改成super(name)就实现了讲线程起名字的目的。



上图中d1.start()改成d1.run()则只是运行run()方法里面的代码,并没有生成新的线程,所以仍然只有main()方法一个线程在执行,所以run方法的里面有一句Thread.currentThread().getName()显示当前正在运行的线程的名字是main,而如果是d1.start( )则Thread.currentThread().getName()的得到的自然是d1这个线程的名字。

4、多线程的内存运行图解如下:

每个线程中都有自己独有的栈区,哪个线程调用方法,那方法就进对应线程的栈空间,一个线程对应一个代码执行路径,就把大的栈空间,再划分为几个小的栈空间,各自线程中的方法和变量在各自的线程栈空间中入栈、出栈。


只要还有在运行的线程,虚拟机就还在运行。main()线程先运行完,main线程就线出栈了。同理,哪个线程抛RuntimeException哪个线程就先结束直接出栈。



重写了继承自object类的finalize方法,使得只要一被回收就打印demo ok。

 

 

创建线程的目的是为了开启一条执行路径,要让一部分代码(任务)能和其他代码同时执行。

 

New出来的线程,它是为了执行任务,而它的任务就是run方法中的代码,所以要把run方法覆盖重写分配线程任务,并让它执行起来。

只有调用了start方法,才算是真正启动了一个线程,java虚拟机会自动调用该线程的run方法。


两个线程之间是随机切换的,说不好那个先执行。

 


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值