包
1、包的介绍
我们书写的Java程序编译后会生成class文件。由于操作系统在同一个目录不能存放相同的文件或者文件夹。
我们在使用Java表述事物的时候,有时也会出现同名的类,那么编译完之后,就会生成同名的class文件,这样就无法在同一个目录下保存。于是Java就给我们提供解决方案,采用不同的文件夹来保存这些同名的类。
在Java中把用于保存class文件的文件夹称为包。
包的作用:用来管理class文件的。
2、包的定义和命名
2.1、Java中定义包:
Java中定义任何一个东西依赖于关键字。Java中使用package关键字定义包。
/*
演示定义包
包的定义使用关键字package
并且它要定义在源文件的第一行
packageone; 给当前的程序定义了一个包
包名为one。Java中规范所有的包名全部小写
当给一个类定义包之后,在编译的时候需要指定生成的class文件的存放位置。
E:\上课视频\JavaSE0729\code\day13_code>javac -d . PackageDemo.java
javac 是启动编译器
-d参数是指明当前生成的class文件存放位置 如果是当前目录下可以使用.
如果不是当前目录下,可以在 -d 后面书写具体的目录
当一个类有了包之后,运行时期,书写的格式:
java 包名.类名 来运行
E:\上课视频\JavaSE0729\code\day13_code>java one.PackageDemo
如果要运行的class文件不在当前目录下,可以把运行的目录切换到class文件所在的目录
如果不想切换,可以设置classpath环境变量
*/
package one;
class PackageDemo
{
public static void main(String[] args)
{
System.out.println("演示Java中的包");
}
}
</pre><p style="background:#F3F3F3;"></p></div><p> </p><h3>2.2、包的命名规则</h3><p> </p><p>在开发中包的命名有规范:</p><p>一般包的名称会和当前包中存放的类的功能一致。</p><p>java.lang java语言的核心包</p><p>java.util Java语言中的所有工具类</p><p> </p><p>在开发中给类定义包的时候,是根据当前类在这个项目中的作用:</p><p>一般会使用公司的域名(公司的官网)来给包命名</p><p><a target=_blank href="http://www.itcast.cn">www.itcast.cn</a> </p><p><a target=_blank href="http://www.baidu.com">www.baidu.com</a> </p><p> </p><p>一般使用的包名是公司的域名倒着来写:</p><p>cn.itcast.后面书写的是当前这个包中存放的类的功能</p><p>com.baidu.xxxx</p><p> </p><p>cn.itcast.domain JavaBean 其实是一个Java类,这个类中仅有get或者set方法而已</p><p>cn.itcast.expcetion 这个包下放的是自定义异常</p><p> </p><p>包名要求必须小写。</p><p> </p><p> </p><p> </p><h2>3、包和包之间访问</h2><p> </p><p></p><p></p><p> </p><p> </p><p> </p><p> </p><p> </p><p></p><p> </p><p>报错的原因是当前One这个类在one包中存放着, 而Two这个类不再One包中,那么就无法直接通过类名来访问不再同一个包中的类。</p><p>要访问不再同一个包中的其他类,必须通过 <span style="color:red;"> </span><span style="color:red;">包名</span><strong><span style="color:red;">.</span></strong><span style="color:red;">类名</span> 的方式来访问</p><p> </p><p>给Two类中访问的One类加上包名</p><p></p><p> </p><p> </p><p></p><p> </p><p>当要访问不再同一个包中的类时,需要使用 包名.类名 同时要求被访问的那个包中的类的权限足够大,保证在这个包以外的其他类能够访问这个类。</p><p>当要访问不在同一个包中的类,这个类的权限必须是public类型。 当一个类被public修饰之后,那么要求这个类的源文件名必须这个类名保持一致。</p><p> </p><p> </p><p></p><p> </p><p></p><p>现在One类是public类型,但是One类中的one方法不是public类型,导致在one包以外的其他类能够访问到One类,但是不能访问one方法</p><p></p><p> </p><p></p><p> </p><p> </p><p> </p><h2>4、包和包之间继承</h2><p> </p><p></p><p> </p><p></p><p> </p><p></p><p> </p><p>包和包之间的继承,要求权限足够大,如果某个类中的方法值允许自己的类访问,这个方法可以使用protected关键字来修饰</p><p> </p><h2>5、访问权限</h2><p> </p><p>访问权限共计4个</p><p> </p><p> </p><table border="1" cellpadding="0" cellspacing="0"> <tbody><tr> <td valign="top"><p> </p></td> <td valign="top"><p>public(谁都可以访问)</p></td> <td valign="top"><p>protected(只让子类访问)</p></td> <td valign="top"><p>default(只能在本包中访问)</p></td> <td valign="top"><p>private(只能在本类中访问)</p></td> </tr> <tr> <td valign="top"><p>同一个类中</p></td> <td valign="top"><p>可以</p></td> <td valign="top"><p>可以</p></td> <td valign="top"><p>可以</p></td> <td valign="top"><p>可以</p></td> </tr> <tr> <td valign="top"><p>同包不同类之间</p></td> <td valign="top"><p>可以</p></td> <td valign="top"><p>可以</p></td> <td valign="top"><p>可以</p></td> <td valign="top"><p>不可以</p></td> </tr> <tr> <td valign="top"><p>不同包不同子类</p></td> <td valign="top"><p>可以</p></td> <td valign="top"><p>可以</p></td> <td valign="top"><p>不可以</p></td> <td valign="top"><p>不可以</p></td> </tr> <tr> <td valign="top"><p>不同包不同类</p></td> <td valign="top"><p>可以</p></td> <td valign="top"><p>不可以</p></td> <td valign="top"><p>不可以</p></td> <td valign="top"><p>不可以</p></td> </tr></tbody></table><p> </p><p> </p><h2>6、导包和jar包</h2><p> </p><h3>6.1、导包介绍</h3><p>当给某个类定义了包之后,在使用这个类的时候需要使用包名.类名的形式,这个使用时十分不方便。</p><p>于是Java给我们提供提前导入包的概念:</p><p>在还没有开始使用这个类之间,先把这个类导入当前使用的类中。</p><p>使用import关键字在类开始的时候提前导入包。</p><p>import one.two.three.demo.Demo;</p><p>如果多个包下有同名的类,只能导入一个,其他的类需要使用包名.类名形式完成</p><p> </p><p>one.two.three.Demo</p><p>one.two.three.Test</p><p>one.two.three.DemoTest</p><p> </p><p>one.two.three.test.Test</p><p> </p><p>一次导入某个包下的所有类</p><p>import one.two.three.*; 把当前three包下的所有类全部导入进来,无法导入three的子包中的类。</p><p> </p><p>JDK5有个特性,静态导入:</p><p>当某个类中有静态成员时,我们要使用这些静态的成员,可以在类上提前把这些静态的成员导入进来。</p><p> </p><p> </p><h3>6.2、jar包</h3><p>在window中可以有压缩文件,在java也给我们提供Java的压缩文件,这个压缩文件称为Jar文件。</p><p> </p><p>格式 jar -cvf jar包名 包名</p><p> </p><h1>多线程</h1><h2>1、进程和线程介绍</h2><p>多线程技术主要是解决的是让多个程序能够同时执行起来。可以提高程序的执行效率。</p><p> </p><h3>1.1、进程</h3><p>正在执行的程序。当我们运行系统上安装的应用程序之后,这个应用程序就会被加载内存中,并且在内存中开始运行。这个应用程序被加载到内存中,</p><p>它需要在内存中分配内存空间。这时就需要分配内存空间,而这个内存空间就是一个进程。这个空间专门负责当前这个应用程序的执行。</p><p>进程其实是专门负责当前这个应用程序的内存空间的分配和管理,以及程序的执行过程的任务调度。</p><p>进程它们是独立的运行单元,在内存中是不会互相影响。</p><p> </p><h3>1.2、线程</h3><p>一个应用程序肯定是由多部分代码组成。而这些代码在当前这个进程中要执行。</p><p>当一个应用程序被启动之后,这个应用程序所占的内存空间又会被划分成多个区域,多个区域来负责运行当前进程中不同功能。</p><p>而负责运行这个功能的那些单独执行空间(执行路径)就称为每个线程。</p><p> </p><p>一个进程中最少要有一个线程。线程才是真正执行应用程序的执行空间。而现在大部分的程序都是多线程程序。</p><p>这样可以保证当前程序的执行效率。</p><p> </p><h2>2、cpu执行线程简介</h2><p>cpu(中央处理器)它是整个电脑的大脑,所有数据的运行都由它完成。它是负责计算和调度正常电脑系统的运行以及其他程序的运行。</p><p> </p><p>cpu执行程序:</p><p>真正cpu执行程序,在某个时刻某个时间点上,cpu只能执行一个线程。而不是同时在执行多个程序。cpu在执行应用程序的时候,它是以时间碎片为概念在多个程序中的多个线程之间来回切换造成。并且cpu的切换速度非常快,导致我们感觉好像是多个程序在同时运行。</p><p> </p><p>是不是在程序开的线程越多越好?</p><p>不是,在cpu的有效的处理能力范围内,多开线程,可以提高效率。</p><p> </p><h2>3、主线程介绍</h2><p>在我们书写任何程序中都一个启动的线程,这个线程主线程。</p><p>在Java中我们书写的程序主要从main方法开始运行。然后当main方法执行完之后这个程序就结束了。</p><p> </p><h2>4、创建线程的方式</h2><p>线程也是属于一类事物,Java对这个事物就会有自己的描述。我们需要在api中找Java是使用哪个类来描述线程这类事物。</p><p>Java中使用Thread这个类来描述线程:</p><p>这个类在java.lang包中。</p><p>Thread类是专门用来描述线程这类事物。它可以负责在程序执行过程单独的开辟出一片内存区域用来运行当前单独分配的任务。</p><p> </p><p><span style="color:black;">创建新执行线程有两种方法。一种方法是将类声明为<code>Thread</code></span> 的子类。该子类应重写<code>Thread</code> 类的 <code>run</code> 方法。接下来可以分配并启动该子类的实例。</p><p> </p><h3>4.1、创建线程的第一种方式:</h3><p>1、书写一个类,必须继承Thread</p><p>2、重写Thread类中的run方法</p><p>3、创建子类的对象</p><p>4、通过子类对象开启线程</p><pre name="code" class="java">
//定义一个类继承Thread
class ThreadDemo extends Thread
{
//重写Thread类中的run方法
public void run()
{
for( int i=0;i<20;i++ )
{
System.out.println("runi="+i);
}
}
//程序都是从main方法开始运行
public static voidmain(String[] args)
{
ThreadDemo d = new ThreadDemo();
//启动这个线程
d.start();
/*
创建的子类对象之后调用start方法
使该线程开始执行;Java 虚拟机调用该线程的 run 方法。
*/
for( int i=0;i<20;i++ )
{
System.out.println("maini="+i);
}
System.out.println("main over....");
}
}
4.2、为什么继承Thread和复写run方法
为什么要继承Thread类?
因为在Java中使用Thread类描述线程这类事物。也就是说Thread类具备了操作线程这类事物的基本行为(功能)。当书写了一个类继承了Thread这个类,
那么我们写的这个子类也就变成线程类了。那么我们书写的这个类也会具备线程的继承操作行为(这些行为其实是从Thread类中继承到的)。我们就可以开启我们自己写的这个线程对象类。
我们定义线程目的是什么?目的是希望在内存中开辟出多条执行路径,让多部分代码能够(并发)同时执行。
如果我们直接new Thread类,而这个Thread是api中已经写好的类,我们无法把自己想让线程执行的代码交给Thread类。那么我们就直接写个类继承Thread,那么我们自己的类也就变成线程类了,那么就可以在自己的类中书写线程要并发执行的代码。
为什么要复写run方法?
run方法也是Thread类中写好的方法。这个方法是在调用start方法开启线程的时候,有JVM自动调用run方法。JVM调用run方法的目的是去执行我们给run方法中分配的需要线程并发执行的那些代码。
复写run方法的目的就是在run方法书写当前线程要执行的任务代码。
如果是不调用start方法,而是创建一个线程对象,直接通过这个对象去调用run方法和使用start间接的去调用run方法有什么区别?
ThreadDemo2 d = new ThreadDemo2();
//启动这个线程
//d.start();
d.run();
在这里new ThreadDemo2 的确是创建了线程对象。但是这个对象直接调用run方法,直接调用run方法的话,就是前面我们所学习对象调用方法是一样的,
这时并没有在内存把这个线程开启,也就是线程存在了,但是它不运行。
5、多线程运行
线程中的异常问题:
当我们在运行多线程程序的时候,如果那个线程发生了异常,那么这个线程所在的代码就会停止运行,但是其他线程不受影响。
红色部分表示异常发生在哪个线程上。黄色部分异常的名字以及异常的发生原因
这时在Thread-0线程上发生了异常,就会导致Thread-0线程停止运行。但是其他线程依然可以正常运行
6、获取线程名字和线程状态
6.1、获取线程的名字:
Thread类是是描述线程本身的,而现在我们又需要获取线程的名字,
猜测有这个方法,猜测返回值可能是String。getName();
在Thread类中的确有getName方法返回当前线程的名字
在Java中如果我们没有手动的指定线程的名字,那么JVM会自动给我们线程分配名字,名字是 Thread-x x从0开始
获取线程名字的方法:
先使用Thread类中的currentThread方法获取到当前正在运行的线程对象,然后再调用getName方法获取线程的名字。
7、实现线程第二种方式
创建线程的另一种方法是声明实现 Runnable
接口的类。该类然后实现 run
方法。然后可以分配该类的实例,在创建 Thread
时作为一个参数来传递并启动.
1、定义类实现Runnable接口
2、实现run方法,这个run方法是接口中的方法
3、创建实现类对象
4、创建Thread类对象,把实现类对象作为参数传递
5、开启线程
当我们要定义一个线程时,目的是给这个线程分配线程运行时要执行的任务代码。Java在设计的时候使用Thread类来描述线程这个事物,这时由在Thread类中定义了一个run方法。而这个run方法是专门用来存放线程要执行的任务。Java在设计Thread类的时候,让线程本身对象和线程要执行的任务严重的耦合在一起。
于是就把这个任务单独的抽离出来,放到一个接口中,这样就可以保证Thread类专门负责线程这个事物,而Runnble接口专门负责线程要执行的任务。
当我们要让线程执行某个任务时,首先我们可以创建Thread本身对象,然后在把任务传递给Thread对象。这样Thread对象就可以执行我们传递的这个任务。
由于Java只支持单继承,如果有一个类中有部分代码需要多线程来执行,而这个类已经继承了其他的类,这时这个类无法在继承Thread类,可是其中需要多线程执行的任务没有办法交给Thread类。这时就可以让这个类实现Runnable接口,然后创建这个类对象,在把这个类对象交给Thread,那么Thread 就可以执行其中的任务。
8、线程练习
模拟售票窗口:
一般情况下火车站有多个窗口负责售票。这些窗口都会同时(并发执行)售票。使用多线程技术来模拟窗口售票的现象。
可以把售票的这个动作认为是线程要操作的任务。这个任务什么时候结束?当把某个趟列车上的票售完之后,窗口就不能在售这趟列车上的票。
我们现在模拟假设有100张票,4个窗口同时来卖。只要有任何一个窗口售完最后一张票,其他窗口保包含当前窗口不能在继续售票。
//定义类来模拟售票的案例,售票的动作要被多线程执行
class Ticket extends Thread{
//定义一个变量用来记录当前的总票数
//由于我们当前的类本身就是线程对象类,在创建当前类对象的时候
//num没有被静态,每个对象中都会有自己的num那么在run方法运行的时候
//就会出现每个run中都有使用自己当前这个对象中的num
//把num变成静态之后,那么所有的Ticket对象就共享这个num成员变量
static int num = 50;
//售票的动作要被多线程执行,就需要把售票的动作放在run方法中
public void run() {
//书写死循环的目的是让任何一个线程进来之后,就不断的售票
//由于是死循环,就会导致线程在不断的售票
while( true )
{
//当num为0的时候,就说明票已经卖完,就不应该在打印数据了
if( num > 0 )
{
//每打印一次,就代表售出一张票
//让任何线程执行到这里都休眠5毫秒
System.out.println(Thread.currentThread().getName()+"....."+num);
num--;
}
}
}
}
class ThreadTest {
public static voidmain(String[] args) {
//创建四个线程对象
Ticket t1 = new Ticket();
Ticket t2 = new Ticket();
Ticket t3 = new Ticket();
Ticket t4 = new Ticket();
//开启线程
t1.start();
t2.start();
t3.start();
t4.start();
}
}